diff --git a/.dockerignore b/.dockerignore index 9bee8e5ba..dd3d41423 100644 --- a/.dockerignore +++ b/.dockerignore @@ -28,10 +28,7 @@ cli-linux **/obj/ **/node_modules/ **/bower_components/ -**/wwwroot/lib/ -!src/Web/WebDashboardRazor/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 c3d9ac687..660f647e7 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 @@ -268,3 +272,6 @@ src/Services/Catalog/Catalog.API/Setup/CatalogItemsAI.zip cli/ /workbench/__pycache__ .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 ceb7268ad..d7edf62bf 100644 --- a/NuGet.config +++ b/NuGet.config @@ -4,7 +4,9 @@ - + + + diff --git a/README.md b/README.md index b7348e4be..4ee99147f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,72 @@ # eShopOnContainersAI -## Definition and goals +## Linux Build Status for 'dev' branch -This repo has a forked version of https://github.com/dotnet-architecture/eShopOnContainers which has been evolved by adding AI and ML features. +Dev branch contains the latest "stable" code, and their images are tagged with `:dev` in our [Docker Hub](https://cloud.docker.com/u/eshop/repository/list): + +Api Gateways base image + +[![Api Gateways base image](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/apigws?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=201&branchName=dev) + +Basket API + +[![Basket API](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/basket?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=199&branchName=dev) + +Catalog API + +[![Catalog API](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/catalog?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=197&branchName=dev) + +Identity API + +[![Identity API](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/identity?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=200&branchName=dev) + +Location API + +[![Location API](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/location?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=202&branchName=dev) + +Marketing API + +[![Marketing API](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/marketing?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=203&branchName=dev) + +Ordering API + +[![Ordering API](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/ordering?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=198&branchName=dev) + +Payment API + +[![Payment API](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/payment?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=205&branchName=dev) + +Webhooks API + +[![Webhooks API](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/webhooks?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=207&branchName=dev) + +Web Shopping Aggregator + +[![Web Shopping Aggregator](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/web-shopping-agg?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=206&branchName=dev) + +Mobile Shopping Aggregator + +[![Mobile Shopping Aggregator](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/mobile-shopping-agg?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=204&branchName=dev) + +Webbhooks demo client + +[![Webhooks demo client](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/webhooks-client?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=208&branchName=dev) + +WebMVC Client + +[![WebMVC Client](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/webmvc?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=209&branchName=dev) + +WebSPA Client + +[![WebSPA Client](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/webspa?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=210&branchName=dev) + +Web Status + +[![Web Status](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/microservices/webstatus?branchName=dev)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=211&branchName=dev) + + +## IMPORTANT NOTES! +**You can use either the latest version of Visual Studio or simply Docker CLI and .NET CLI for Windows, Mac and Linux**. *eShopOnContainers* is a cloud-native application based on microservices architecture and Docker containers. *eShopOnContainersAI* is therefore a forked version of eShopOnContainers that is extended with AI features (Machine Learning and Deep Learning) plus a Bot client as a new client app which surfaces all the AI features along with the modified MVC web application. @@ -15,6 +79,9 @@ Main AI/ML technologies used are: - **CNTK** - **Bot Framework** +## 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. + Here's a vision of the architecture where the grayed area is what is coming derived from https://github.com/dotnet-architecture/eShopOnContainers and the rest of the diagram is about the new added AI features. ![image](https://user-images.githubusercontent.com/1712635/42792118-bc21b18e-8928-11e8-9084-a5a6af87c8ba.png) @@ -25,6 +92,27 @@ This diagram will be updated soon. The following diagram positions the multiple AI technologies per AI function and type: ![image](https://user-images.githubusercontent.com/1222398/36477436-746362e6-1701-11e8-9312-52faecbda715.png) +>**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 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 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. + +![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. + +### 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. +

+ + +

You will learn how to use Pre-Built models (such as in Cognitive Services), Pre-Trained and Custom models to add AI and ML features into any application: @@ -32,10 +120,81 @@ You will learn how to use Pre-Built models (such as in Cognitive Services), Pre- * Recommendation systems: One of the most used cases, recommend products from the basket, will be used as example of these models. * Natural Language Processing: Bots are the corner stone of current AI applications. You will learn how to create new solutions based in BOT framework, integrate bots in your current applications, or use L.U.I.S. to get information about user intents, * Computer Vision: These models gained much traction in current decade, and industry is investing large amount of resources in this field. Using different strategies, you will learn how to search for similar images, using Cognitive Services or deploying your own custom trained models. +> 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. + + +> The sample code in this repo is NOT making use of Azure API Management in order to be able to provide an "F5 experience" in Visual Studio (or CLI) of the sample with no up-front dependencies in Azure. But you could evaluate API Gateways alternatives when building for production. + +> ### Internal architecture and design of the microservices + +> The microservices are different in type, meaning different internal architecture pattern approaches depending on its purpose, as shown in the image below. +

+ +

+

+ +> ### 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 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 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. +

+There are also additional eBooks focusing on Containers/Docker lifecycle (DevOps, CI/CD, etc.) with Microsoft Tools, already published plus an additional eBook focusing on Enterprise Apps Patterns with Xamarin.Forms. +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.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 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 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. +- Open eShopOnContainers-MobileApps.sln for a solution containing just the client mobile app projects (Xamarin mobile apps only). It works independently based on mocks, too. +- Open eShopOnContainers.sln for a solution containing all the projects (All client apps and services). + + + +Finally, those microservices are consumed by multiple client web and mobile apps, as described below. +
+*MVC Application (ASP.NET Core)*: It's an MVC application where you can find interesting scenarios on how to consume HTTP-based microservices from C# running in the server side, as it is a typical ASP.NET Core MVC application. Since it is a server-side application, access to other containers/microservices is done within the internal Docker Host network with its internal name resolution. + +
+*SPA (Single Page Application)*: Providing similar "eShop business functionality" but developed with Angular, Typescript and slightly using ASP.NET Core MVC. This is another approach for client web applications to be used when you want to have a more modern client behavior which is not behaving with the typical browser round-trip on every action but behaving like a Single-Page-Application which is more similar to a desktop app usage experience. The consumption of the HTTP-based microservices is done from TypeScript/JavaScript in the client browser, so the client calls to the microservices come from out of the Docker Host internal network (Like from your network or even from the Internet). + +
+*Xamarin Mobile App (For iOS, Android and Windows/UWP)*: It is a client mobile app supporting the most common mobile OS platforms (iOS, Android and Windows/UWP). In this case, the consumption of the microservices is done from C# but running on the client devices, so out of the Docker Host internal network (Like from your network or even the Internet). + + + +## Setting up your development environment for eShopOnContainers +### Visual Studio 2017 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) +### 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) -See Wiki for how set it up and see the multiple scenarios: -https://github.com/dotnet-architecture/eShopOnContainersAI/wiki +## 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 We'd appreciate your feedback, improvements and ideas. diff --git a/build/azure-devops/apigws/azure-pipelines.yml b/build/azure-devops/apigws/azure-pipelines.yml new file mode 100644 index 000000000..d2f4708f6 --- /dev/null +++ b/build/azure-devops/apigws/azure-pipelines.yml @@ -0,0 +1,50 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/ApiGateways/* + - k8s/helm/apigwmm/* + - k8s/helm/apigwms/* + - k8s/helm/apigwwm/* + - k8s/helm/apigwws/* + exclude: + - src/ApiGateways/Mobile.Bff.Shopping/aggregator/* + - src/ApiGateways/Web.Bff.Shopping/aggregator/* +steps: +- task: DockerCompose@0 + displayName: Compose build apigws + inputs: + dockerComposeCommand: 'build mobileshoppingapigw mobilemarketingapigw webshoppingapigw webmarketingapigw' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push apigws + inputs: + dockerComposeCommand: 'push mobileshoppingapigw mobilemarketingapigw webshoppingapigw webmarketingapigw' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/basket-api/azure-pipelines.yml b/build/azure-devops/basket-api/azure-pipelines.yml new file mode 100644 index 000000000..6d94c5918 --- /dev/null +++ b/build/azure-devops/basket-api/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Services/Basket/* + - k8s/helm/basket-api/* +steps: +- task: DockerCompose@0 + displayName: Compose build basket + inputs: + dockerComposeCommand: 'build basket.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push basket + inputs: + dockerComposeCommand: 'push basket.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/catalog-api/azure-pipelines.yml b/build/azure-devops/catalog-api/azure-pipelines.yml new file mode 100644 index 000000000..d277d4777 --- /dev/null +++ b/build/azure-devops/catalog-api/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Services/Catalog/* + - k8s/helm/catalog-api/* +steps: +- task: DockerCompose@0 + displayName: Compose build catalog + inputs: + dockerComposeCommand: 'build catalog.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push catalog + inputs: + dockerComposeCommand: 'push catalog.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/identity-api/azure-pipelines.yml b/build/azure-devops/identity-api/azure-pipelines.yml new file mode 100644 index 000000000..454c03d16 --- /dev/null +++ b/build/azure-devops/identity-api/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Services/Identity/* + - k8s/helm/identity-api/* +steps: +- task: DockerCompose@0 + displayName: Compose build identity + inputs: + dockerComposeCommand: 'build identity.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push identity + inputs: + dockerComposeCommand: 'push identity.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/location-api/azure-pipelines.yml b/build/azure-devops/location-api/azure-pipelines.yml new file mode 100644 index 000000000..ab3d31b3f --- /dev/null +++ b/build/azure-devops/location-api/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Services/Location/* + - k8s/helm/locations-api/* +steps: +- task: DockerCompose@0 + displayName: Compose build locations + inputs: + dockerComposeCommand: 'build locations.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push locations + inputs: + dockerComposeCommand: 'push locations.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/marketing-api/azure-pipelines.yml b/build/azure-devops/marketing-api/azure-pipelines.yml new file mode 100644 index 000000000..193a0cccf --- /dev/null +++ b/build/azure-devops/marketing-api/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Services/Marketing/* + - k8s/helm/marketing-api/* +steps: +- task: DockerCompose@0 + displayName: Compose build marketing + inputs: + dockerComposeCommand: 'build marketing.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push marketing + inputs: + dockerComposeCommand: 'push marketing.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/mobile-shopping-agg/azure-pipelines.yml b/build/azure-devops/mobile-shopping-agg/azure-pipelines.yml new file mode 100644 index 000000000..6fb0018a1 --- /dev/null +++ b/build/azure-devops/mobile-shopping-agg/azure-pipelines.yml @@ -0,0 +1,44 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/ApiGateways/Mobile.Bff.Shopping/aggregator/* + - k8s/helm/mobileshoppingagg/* +steps: +- task: DockerCompose@0 + displayName: Compose build mobileshoppingagg + inputs: + dockerComposeCommand: 'build mobileshoppingagg' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push mobileshoppingagg + inputs: + dockerComposeCommand: 'push mobileshoppingagg' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/ordering-api/azure-pipelines.yml b/build/azure-devops/ordering-api/azure-pipelines.yml new file mode 100644 index 000000000..ef0b17a32 --- /dev/null +++ b/build/azure-devops/ordering-api/azure-pipelines.yml @@ -0,0 +1,47 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Services/Ordering/* + - k8s/helm/ordering-api/* + - k8s/helm/ordering-backgroundtasks/* + - k8s/helm/ordering-signalrhub/* +steps: +- task: DockerCompose@0 + displayName: Compose build ordering + inputs: + dockerComposeCommand: 'build ordering.api ordering.backgroundtasks ordering.signalrhub' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push ordering + inputs: + dockerComposeCommand: 'push ordering.api ordering.backgroundtasks ordering.signalrhub' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/payment-api/azure-pipelines.yml b/build/azure-devops/payment-api/azure-pipelines.yml new file mode 100644 index 000000000..8518c8841 --- /dev/null +++ b/build/azure-devops/payment-api/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Services/Payment/* + - k8s/helm/payment-api/* +steps: +- task: DockerCompose@0 + displayName: Compose build payment + inputs: + dockerComposeCommand: 'build payment.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push payment + inputs: + dockerComposeCommand: 'push payment.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/readme.md b/build/azure-devops/readme.md new file mode 100644 index 000000000..b7216d4de --- /dev/null +++ b/build/azure-devops/readme.md @@ -0,0 +1,5 @@ +# Azure Devops build definitions + +This folder contains the Azure Devops build definitions in YAML format. Each folder contains one `azure-pipelines.yml` that contains the build definition for one microservice (usually a Docker image, but some microservices generates more than one Docker image). + +For more information about YAML builds read the [Azure DevOps documentation](https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started-yaml?view=azure-devops). \ No newline at end of file diff --git a/build/azure-devops/web-shopping-agg/azure-pipelines.yml b/build/azure-devops/web-shopping-agg/azure-pipelines.yml new file mode 100644 index 000000000..70a4df950 --- /dev/null +++ b/build/azure-devops/web-shopping-agg/azure-pipelines.yml @@ -0,0 +1,44 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/ApiGateways/Web.Bff.Shopping/aggregator/* + - k8s/helm/webshoppingagg/* +steps: +- task: DockerCompose@0 + displayName: Compose build webshoppingagg + inputs: + dockerComposeCommand: 'build webshoppingagg' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push webshoppingagg + inputs: + dockerComposeCommand: 'push webshoppingagg' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/webhooks-api/azure-pipelines.yml b/build/azure-devops/webhooks-api/azure-pipelines.yml new file mode 100644 index 000000000..b48948111 --- /dev/null +++ b/build/azure-devops/webhooks-api/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Services/Webhooks/* + - k8s/helm/webhooks-api/* +steps: +- task: DockerCompose@0 + displayName: Compose build webhooks + inputs: + dockerComposeCommand: 'build webhooks.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push webhooks + inputs: + dockerComposeCommand: 'push webhooks.api' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/webhooks-client/azure-pipelines.yml b/build/azure-devops/webhooks-client/azure-pipelines.yml new file mode 100644 index 000000000..71e3bbe16 --- /dev/null +++ b/build/azure-devops/webhooks-client/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Web/WebhookClient/* + - k8s/helm/webhooks-web/* +steps: +- task: DockerCompose@0 + displayName: Compose build webhooks.client + inputs: + dockerComposeCommand: 'build webhooks.client' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push webhooks.client + inputs: + dockerComposeCommand: 'push webhooks.client' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/webmvc/azure-pipelines.yml b/build/azure-devops/webmvc/azure-pipelines.yml new file mode 100644 index 000000000..66b8518fa --- /dev/null +++ b/build/azure-devops/webmvc/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Web/WebMVC/* + - k8s/helm/webmvc/* +steps: +- task: DockerCompose@0 + displayName: Compose build webmvc + inputs: + dockerComposeCommand: 'build webmvc' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push webmvc + inputs: + dockerComposeCommand: 'push webmvc' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/webspa/azure-pipelines.yml b/build/azure-devops/webspa/azure-pipelines.yml new file mode 100644 index 000000000..eee8cd25d --- /dev/null +++ b/build/azure-devops/webspa/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Web/WebSPA/* + - k8s/helm/webspa/* +steps: +- task: DockerCompose@0 + displayName: Compose build webspa + inputs: + dockerComposeCommand: 'build webspa' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push webspa + inputs: + dockerComposeCommand: 'push webspa' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm diff --git a/build/azure-devops/webstatus/azure-pipelines.yml b/build/azure-devops/webstatus/azure-pipelines.yml new file mode 100644 index 000000000..311c457e2 --- /dev/null +++ b/build/azure-devops/webstatus/azure-pipelines.yml @@ -0,0 +1,45 @@ +pool: + vmImage: 'ubuntu-16.04' +variables: + registryEndpoint: eshop-registry +trigger: + branches: + include: + - master + - dev + paths: + include: + - src/BuildingBlocks/* + - src/Web/WebStatus/* + - k8s/helm/webstatus/* +steps: +- task: DockerCompose@0 + displayName: Compose build webstatus + inputs: + dockerComposeCommand: 'build webstatus' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: DockerCompose@0 + displayName: Compose push webstatus + inputs: + dockerComposeCommand: 'push webstatus' + containerregistrytype: Container Registry + dockerRegistryEndpoint: $(registryEndpoint) + dockerComposeFile: docker-compose.yml + qualifyImageNames: true + projectName: "" + dockerComposeFileArgs: | + TAG=$(Build.SourceBranchName) +- task: CopyFiles@2 + inputs: + sourceFolder: $(Build.SourcesDirectory)/k8s/helm + targetFolder: $(Build.ArtifactStagingDirectory)/k8s/helm +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)/k8s/helm + artifactName: helm 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 d5b0ca756..e6e9452ef 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -7,21 +7,69 @@ 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 + - ACCEPT_EULA=Y + ports: + - "5433:1433" # Important: In a production environment your should remove the external port + + nosql.data: + ports: + - "27017:27017" # Important: In a production environment your should remove the external port + + basket.data: + ports: + - "6379:6379" # Important: In a production environment your should remove the external port + + rabbitmq: + ports: + - "15672:15672" # Important: In a production environment your should remove the external port + - "5672:5672" # Important: In a production environment your should remove the external port + + identity.api: + 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 #localhost do not work for UWP login, so we have to use "external" IP always + - ConnectionString=${ESHOP_AZURE_IDENTITY_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word} + - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5110. + - 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 + - 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} + ports: + - "5105:80" + basket.api: environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - ConnectionString: ${ESHOP_AZURE_REDIS_BASKET_DB:-basket.data} - identityUrl: http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. - IdentityUrlExternal: http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 - EventBusConnection: ${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - EventBusUserName: ${ESHOP_SERVICE_BUS_USERNAME} - EventBusPassword: ${ESHOP_SERVICE_BUS_PASSWORD} - AzureServiceBusEnabled: "False" - ApplicationInsights__InstrumentationKey: ${INSTRUMENTATION_KEY} - OrchestratorType: ${ORCHESTRATOR_TYPE} - UseLoadTest: ${USE_LOADTEST:-False} + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=${ESHOP_AZURE_REDIS_BASKET_DB:-basket.data} + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} + - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME} + - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD} + - AzureServiceBusEnabled=False + - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} + - OrchestratorType=${ORCHESTRATOR_TYPE} + - UseLoadTest=${USE_LOADTEST:-False} + ports: - "51032:22" - "5103:80" # Important: In a production environment your should remove the external port (5103) kept here for microservice debugging purposes. @@ -78,113 +126,96 @@ services: catalog.api: environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - ConnectionString: "${ESHOP_AZURE_CATALOG_DB:-Server=sql.data;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/}" #Local: You need to open your local dev-machine firewall at range 5100-5110. - EventBusConnection: ${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - EventBusUserName: ${ESHOP_SERVICE_BUS_USERNAME} - EventBusPassword: ${ESHOP_SERVICE_BUS_PASSWORD} - AzureStorageAccountName: ${ESHOP_AZURE_STORAGE_CATALOG_NAME} - AzureStorageAccountKey: ${ESHOP_AZURE_STORAGE_CATALOG_KEY} - UseCustomizationData: "True" - UseCustomizationDataAI: "True" - AzureServiceBusEnabled: "False" - AzureStorageEnabled: "False" - ApplicationInsights__InstrumentationKey: ${INSTRUMENTATION_KEY} - MongoConnectionString: ${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} - MongoDatabase: "CatalogTagDb" - OrchestratorType: ${ORCHESTRATOR_TYPE} + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql.data;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/} #Local: You need to open your local dev-machine firewall at range 5100-5110. + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} + - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME} + - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD} + - AzureStorageAccountName=${ESHOP_AZURE_STORAGE_CATALOG_NAME} + - AzureStorageAccountKey=${ESHOP_AZURE_STORAGE_CATALOG_KEY} + - UseCustomizationData=True + - UseCustomizationDataAI=True + - AzureServiceBusEnabled=False + - AzureStorageEnabled=False + - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} + - MongoConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} + - MongoDatabase=CatalogTagDb + - OrchestratorType=${ORCHESTRATOR_TYPE} ports: - "51012:22" - "5101:80" # Important: In a production environment your should remove the external port (5101) kept here for microservice debugging purposes. # The API Gateway redirects and access through the internal port (80). - identity.api: - environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - # BotClient: https://your-subdomain.ngrok.io - SpaClient: http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104 - XamarinCallback: http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback #localhost do not work for UWP login, so we have to use "external" IP always - ConnectionString: "${ESHOP_AZURE_IDENTITY_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word}" - MvcClient: http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5110. - 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" - UseCustomizationDataAI: "True" - ApplicationInsights__InstrumentationKey: ${INSTRUMENTATION_KEY} - OrchestratorType: ${ORCHESTRATOR_TYPE} - ports: - - "5105:80" - - "51052:22" - ordering.api: environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - ConnectionString: "${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word}" - identityUrl: http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. - IdentityUrlExternal: http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 - EventBusConnection: ${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - EventBusUserName: ${ESHOP_SERVICE_BUS_USERNAME} - EventBusPassword: ${ESHOP_SERVICE_BUS_PASSWORD} - UseCustomizationData: "True" - UseCustomizationDataAI: "True" - AzureServiceBusEnabled: "False" - CheckUpdateTime: 30000 - ApplicationInsights__InstrumentationKey: ${INSTRUMENTATION_KEY} - OrchestratorType: ${ORCHESTRATOR_TYPE} - UseLoadTest: ${USE_LOADTEST:-False} + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + # BotClient: https://your-subdomain.ngrok.io + - SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104 + - XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback #localhost do not work for UWP login, so we have to use "external" IP always + - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word} + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} + - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME} + - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD} + - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5110. + - 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" + - UseCustomizationDataAI="True" + - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} + - OrchestratorType=${ORCHESTRATOR_TYPE} + - UseLoadTest=${USE_LOADTEST:-False} ports: - "51022:22" - - "5102:80" # Important: In a production environment your should remove the external port (5102) kept here for microservice debugging purposes. - # The API Gateway redirects and access through the internal port (80). - - + - "5102:80" + ordering.backgroundtasks: environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - ConnectionString: "${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word}" - EventBusConnection: ${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - EventBusUserName: ${ESHOP_SERVICE_BUS_USERNAME} - EventBusPassword: ${ESHOP_SERVICE_BUS_PASSWORD} - UseCustomizationData: "True" - AzureServiceBusEnabled: "False" - CheckUpdateTime: 30000 - GracePeriodTime: 1 - ApplicationInsights__InstrumentationKey: ${INSTRUMENTATION_KEY} - OrchestratorType: ${ORCHESTRATOR_TYPE} - UseLoadTest: ${USE_LOADTEST:-False} + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word} + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} + - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME} + - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD} + - UseCustomizationData=True + - AzureServiceBusEnabled=False + - CheckUpdateTime=30000 + - GracePeriodTime=1 + - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} + - OrchestratorType=${ORCHESTRATOR_TYPE} + - UseLoadTest=${USE_LOADTEST:-False} ports: - "5111:80" marketing.api: environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - ConnectionString: "${ESHOP_AZURE_MARKETING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word}" - MongoConnectionString: ${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} - MongoDatabase: "MarketingDb" - EventBusConnection: ${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - EventBusUserName: ${ESHOP_SERVICE_BUS_USERNAME} - EventBusPassword: ${ESHOP_SERVICE_BUS_PASSWORD} - identityUrl: http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. - 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} + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=${ESHOP_AZURE_MARKETING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word} + - MongoConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} + - MongoDatabase=MarketingDb + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} + - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME} + - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD} + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - 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" # Important: In a production environment your should remove the external port (5110) kept here for microservice debugging purposes. # The API Gateway redirects and access through the internal port (80). @@ -250,62 +281,66 @@ services: payment.api: environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - EventBusConnection: ${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - EventBusUserName: ${ESHOP_SERVICE_BUS_USERNAME} - EventBusPassword: ${ESHOP_SERVICE_BUS_PASSWORD} - AzureServiceBusEnabled: "False" - ApplicationInsights__InstrumentationKey: ${INSTRUMENTATION_KEY} - OrchestratorType: ${ORCHESTRATOR_TYPE} + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} + - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME} + - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD} + - AzureServiceBusEnabled=False + - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} + - OrchestratorType=${ORCHESTRATOR_TYPE} ports: - "5108:80" # Important: In a production environment your should remove the external port (5108) kept here for microservice debugging purposes. # The API Gateway redirects and access through the internal port (80). locations.api: environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - ConnectionString: ${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} - Database: "LocationsDb" - identityUrl: http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. - IdentityUrlExternal: http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 - EventBusConnection: ${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - EventBusUserName: ${ESHOP_SERVICE_BUS_USERNAME} - EventBusPassword: ${ESHOP_SERVICE_BUS_PASSWORD} - AzureServiceBusEnabled: "False" - ApplicationInsights__InstrumentationKey: ${INSTRUMENTATION_KEY} - OrchestratorType: ${ORCHESTRATOR_TYPE} - UseLoadTest: ${USE_LOADTEST:-False} + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} + - Database=LocationsDb + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} + - 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" # 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). - - sql.data: + webhooks.api: environment: - SA_PASSWORD: "Pass@word" - ACCEPT_EULA: "Y" - ports: - - "5433:1433" # Important: In a production environment your should remove the external port - - nosql.data: + - 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: - - "27017:27017" # Important: In a production environment your should remove the external port - - basket.data: - ports: - - "6379:6379" # Important: In a production environment your should remove the external port - - rabbitmq: - ports: - - "15672:15672" # Important: In a production environment your should remove the external port - - "5672:5672" # Important: In a production environment your should remove the external port + - "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. + - 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: @@ -313,8 +348,15 @@ services: mobilemarketingapigw: environment: - ASPNETCORE_ENVIRONMENT: "Development" - IdentityUrl: http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - 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: @@ -322,8 +364,15 @@ services: webshoppingapigw: environment: - ASPNETCORE_ENVIRONMENT: "Development" - IdentityUrl: http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - 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: @@ -331,8 +380,15 @@ services: webmarketingapigw: environment: - ASPNETCORE_ENVIRONMENT: "Development" - IdentityUrl: http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - 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: @@ -340,22 +396,36 @@ services: mobileshoppingagg: environment: - ASPNETCORE_ENVIRONMENT: "Development" - urls__basket: http://basket.api - 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. + - ASPNETCORE_ENVIRONMENT=Development + - urls__basket=http://basket.api + - 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). webshoppingagg: environment: - ASPNETCORE_ENVIRONMENT: "Development" - urls__basket: http://basket.api - 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. + - ASPNETCORE_ENVIRONMENT=Development + - urls__basket=http://basket.api + - 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). @@ -371,14 +441,104 @@ services: ordering.signalrhub: environment: - ASPNETCORE_ENVIRONMENT: "Development" - ASPNETCORE_URLS: http://0.0.0.0:80 - EventBusConnection: ${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - EventBusUserName: ${ESHOP_SERVICE_BUS_USERNAME} - EventBusPassword: ${ESHOP_SERVICE_BUS_PASSWORD} - AzureServiceBusEnabled: "False" - ApplicationInsights__InstrumentationKey: ${INSTRUMENTATION_KEY} - OrchestratorType: ${ORCHESTRATOR_TYPE} - identityUrl: http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} + - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME} + - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD} + - AzureServiceBusEnabled=False + - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} + - OrchestratorType=${ORCHESTRATOR_TYPE} + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. ports: - "5112:80" + + webstatus: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - 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 + - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} + - OrchestratorType=${ORCHESTRATOR_TYPE} + ports: + - "5107:80" + + webspa: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - 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 + - PurchaseUrlHC=http://webshoppingapigw/hc + - MarketingUrlHC=http://webmarketingapigw/hc + - IdentityUrlHC=http://identity.api/hc + - UseCustomizationData=True + - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} + - OrchestratorType=${ORCHESTRATOR_TYPE} + - SignalrHubUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5202 + ports: + - "5104:80" + + webmvc: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - 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 + - 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} + - UseLoadTest=${USE_LOADTEST:-False} + 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" diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 7e2af2320..b6a41bbc1 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -154,6 +154,25 @@ services: ports: - "5100:80" + webstatus: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - CatalogUrl=http://catalog.api/hc + - OrderingUrl=http://ordering.api/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: + - "5107:80" + payment.api: environment: - ASPNETCORE_ENVIRONMENT=Development diff --git a/docker-compose.windows.yml b/docker-compose.windows.yml index 4b2eb1a9b..afc7e4e91 100644 --- a/docker-compose.windows.yml +++ b/docker-compose.windows.yml @@ -5,33 +5,33 @@ version: '3.4' services: + sql.data: + image: microsoft/mssql-server-windows-developer + + nosql.data: + image: mongo:windowsservercore + + basket.data: + image: redis:nanoserver + + rabbitmq: + image: spring2/rabbitmq + identity.api: build: args: NODE_IMAGE: stefanscherer/node-windows:8.11 - webmvc: + webspa: build: args: NODE_IMAGE: stefanscherer/node-windows:8.11 - - webspa: + + webmvc: build: args: NODE_IMAGE: stefanscherer/node-windows:8.11 - sql.data: - image: microsoft/mssql-server-windows-developer - - nosql.data: - image: mongo:windowsservercore - - basket.data: - image: redis:nanoserver - - rabbitmq: - image: spring2/rabbitmq - networks: default: diff --git a/docker-compose.yml b/docker-compose.yml index a6d795bfd..1ec5bd54c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,10 @@ version: '3.4' services: + + seq: + image: datalust/seq:latest + sql.data: image: microsoft/mssql-server-linux:2017-latest volumes: @@ -121,7 +125,7 @@ services: dockerfile: src/Services/Payment/Payment.API/Dockerfile depends_on: - rabbitmq - + locations.api: image: eshopai/locations.api:${TAG:-latest} build: @@ -131,6 +135,15 @@ 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: eshopai/ocelotapigw:${TAG:-latest} build: @@ -195,7 +208,7 @@ services: - basket.api mobileshoppingagg: - image: eshopai/mobileshoppingagg:${TAG:-latest} + image: ${REGISTRY:-eshop}/mobileshoppingagg:${TAG:-latest} build: context: . dockerfile: src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile @@ -210,7 +223,7 @@ services: - basket.api webshoppingagg: - image: eshopai/webshoppingagg:${TAG:-latest} + image: ${REGISTRY:-eshop}/webshoppingagg:${TAG:-latest} build: context: . dockerfile: src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile @@ -288,3 +301,36 @@ services: - webmarketingapigw - webaiapigw + webstatus: + image: ${REGISTRY:-eshop}/webstatus:${TAG:-latest} + build: + context: . + dockerfile: src/Web/WebStatus/Dockerfile + + webspa: + image: ${REGISTRY:-eshop}/webspa:${TAG:-latest} + build: + context: . + dockerfile: src/Web/WebSPA/Dockerfile +# depends_on: +# - webshoppingagg +# - webshoppingapigw +# - webmarketingapigw + + webmvc: + image: ${REGISTRY:-eshop}/webmvc:${TAG:-latest} + build: + context: . + dockerfile: src/Web/WebMVC/Dockerfile + depends_on: + - webshoppingagg + - 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.01.pdf b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.1.01.pdf new file mode 100644 index 000000000..d3868bd9e Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.1.01.pdf 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-for-Kindle)-v1.1.mobi b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v1.1.mobi new file mode 100644 index 000000000..728f15755 Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v1.1.mobi 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 728f15755..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)-v1.1.epub b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v1.1.epub new file mode 100644 index 000000000..5d4a8b491 Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v1.1.epub 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 5d4a8b491..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/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-eBook).epub b/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-eBook).epub new file mode 100644 index 000000000..b17dcf655 Binary files /dev/null and b/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-eBook).epub differ diff --git a/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-for-Kindle).mobi b/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-for-Kindle).mobi new file mode 100644 index 000000000..0a2a20ca6 Binary files /dev/null and b/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-for-Kindle).mobi differ diff --git a/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-for-eReader).pdf b/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-for-eReader).pdf new file mode 100644 index 000000000..02db1070b Binary files /dev/null and b/docs/Serverless-apps-Architecture-patterns-and-Azure-implementation-(Microsoft-for-eReader).pdf differ diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index f9e3e654e..447b73073 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ordering", "Ordering", "{0B EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A857AD10-40FF-4303-BEC2-FF1C58D5735E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{EF0337F2-ED00-4643-89FD-EE10863F1870}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ServicesTests", "ServicesTests", "{EF0337F2-ED00-4643-89FD-EE10863F1870}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{24CD3B53-141E-4A07-9B0D-796641E1CF78}" EndProject @@ -42,16 +42,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebMVC", "src\Web\WebMVC\We EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.Infrastructure", "src\Services\Ordering\Ordering.Infrastructure\Ordering.Infrastructure.csproj", "{95F1F07C-4D92-4742-BD07-E5B805AAB651}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\UnitTest\UnitTest.csproj", "{7796F5D8-31FC-45A4-B673-19DE5BA194CF}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj", "{F16E3C6A-1C94-4EAB-BE91-099618060B68}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "test\Services\IntegrationTests\IntegrationTests.csproj", "{5B810E3D-112E-4857-B197-F09D2FD41E27}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\Services\FunctionalTests\FunctionalTests.csproj", "{CFE2FACB-4538-4B99-8A10-306F3882952D}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{DB0EFB20-B024-4E5E-A75C-52143C131D25}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventBus", "EventBus", "{807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}" @@ -62,20 +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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E145D-1593-47EE-9608-8E323D3C63F5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}" -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}" @@ -86,10 +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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoadTest", "test\Services\LoadTest\LoadTest.csproj", "{969E793C-C413-490E-9C9D-B2B46DA5AF32}" -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}" @@ -128,6 +106,46 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.BackgroundTasks", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.SignalrHub", "src\Services\Ordering\Ordering.SignalrHub\Ordering.SignalrHub.csproj", "{E1D2B260-4E7F-4A88-BC13-9910F7C44623}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basket.UnitTests", "src\Services\Basket\Basket.UnitTests\Basket.UnitTests.csproj", "{9D9CE4E4-1DD0-4961-861F-219731DE06CE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalog.UnitTests", "src\Services\Catalog\Catalog.UnitTests\Catalog.UnitTests.csproj", "{791961C7-3F3E-434E-B2BA-B4D6B5E222B0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basket.FunctionalTests", "src\Services\Basket\Basket.FunctionalTests\Basket.FunctionalTests.csproj", "{3F6202D0-2842-4C2F-98E1-9462709EAFBE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.UnitTests", "src\Services\Ordering\Ordering.UnitTests\Ordering.UnitTests.csproj", "{B1182FD9-C245-4018-8412-C66F290C7F4C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalog.FunctionalTests", "src\Services\Catalog\Catalog.FunctionalTests\Catalog.FunctionalTests.csproj", "{38107691-A437-461D-A85C-ACD3AC7ACFAB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Locations.FunctionalTests", "src\Services\Location\Locations.FunctionalTests\Locations.FunctionalTests.csproj", "{16F463AA-9CF6-44DC-B18C-7310CCF663FF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.FunctionalTests", "src\Services\Ordering\Ordering.FunctionalTests\Ordering.FunctionalTests.csproj", "{DA7D3E03-D0B6-4591-8143-779D3E9F3F30}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.FunctionalTests", "src\Services\Marketing\Marketing.FunctionalTests\Marketing.FunctionalTests.csproj", "{94176D9B-9CAA-4762-8D12-1621E240EE34}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoadTest", "test\ServicesTests\LoadTest\LoadTest.csproj", "{969E793C-C413-490E-9C9D-B2B46DA5AF32}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application.FunctionalTests", "test\ServicesTests\Application.FunctionalTests\Application.FunctionalTests.csproj", "{639BB197-D112-47A7-A44A-471DDB0FA1AE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2751AC5C-D148-4D7A-AE8F-149B47C9A82D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{5FB21302-3973-4992-962A-6F87F5EC99FD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C3F6ED48-E26D-4D57-970F-B82E69467BA1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{32EE4736-7534-47EC-BAAD-C00AF3130F80}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{120CABB3-0FEA-4B40-B4B5-2D3041798C80}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{C61C5CFE-4876-4A46-A96E-5BBF596A984A}" +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 @@ -382,54 +400,6 @@ Global {95F1F07C-4D92-4742-BD07-E5B805AAB651}.Release|x64.Build.0 = Release|Any CPU {95F1F07C-4D92-4742-BD07-E5B805AAB651}.Release|x86.ActiveCfg = Release|Any CPU {95F1F07C-4D92-4742-BD07-E5B805AAB651}.Release|x86.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|ARM.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhone.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x64.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x64.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x86.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x86.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|ARM.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|ARM.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhone.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x64.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x64.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x86.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x86.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|Any CPU.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|ARM.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|ARM.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhone.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhone.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x64.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x64.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x86.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x86.Build.0 = Release|Any CPU {A579E108-5445-403D-A407-339AC4D1611B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {A579E108-5445-403D-A407-339AC4D1611B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {A579E108-5445-403D-A407-339AC4D1611B}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -526,102 +496,6 @@ Global {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x64.Build.0 = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x86.ActiveCfg = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x86.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|ARM.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|iPhone.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|x64.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|x64.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|x86.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|x86.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|ARM.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|ARM.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|iPhone.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|x64.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|x64.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|x86.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|x86.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|Any CPU.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|ARM.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|ARM.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|iPhone.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|iPhone.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|x64.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|x64.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|x86.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|x86.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|ARM.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|iPhone.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|x64.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|x64.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|x86.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|x86.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|ARM.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|ARM.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|iPhone.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|x64.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|x64.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|x86.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|x86.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|Any CPU.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|ARM.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|ARM.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|iPhone.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|iPhone.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x64.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x64.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x86.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x86.Build.0 = Release|Any CPU {0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -766,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 @@ -862,150 +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 - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|ARM.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhone.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x64.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x64.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x86.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x86.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|ARM.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|ARM.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhone.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x64.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x64.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x86.ActiveCfg = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x86.Build.0 = Debug|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|Any CPU.Build.0 = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|ARM.ActiveCfg = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|ARM.Build.0 = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhone.ActiveCfg = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhone.Build.0 = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.ActiveCfg = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.Build.0 = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.ActiveCfg = Release|Any CPU - {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.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 @@ -1150,102 +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 - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|ARM.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x64.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x86.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|Any CPU.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|ARM.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|ARM.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhone.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x64.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x64.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x86.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x86.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|Any CPU.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|ARM.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|ARM.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhone.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x64.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x64.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x86.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x86.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|Any CPU.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|Any CPU.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|ARM.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|ARM.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhone.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhone.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x64.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x64.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x86.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.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 @@ -1534,6 +1120,630 @@ Global {E1D2B260-4E7F-4A88-BC13-9910F7C44623}.Release|x64.Build.0 = Release|Any CPU {E1D2B260-4E7F-4A88-BC13-9910F7C44623}.Release|x86.ActiveCfg = Release|Any CPU {E1D2B260-4E7F-4A88-BC13-9910F7C44623}.Release|x86.Build.0 = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|ARM.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|iPhone.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|x64.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|x64.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|x86.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.AppStore|x86.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|ARM.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|ARM.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|iPhone.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|x64.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|x64.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|x86.ActiveCfg = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Debug|x86.Build.0 = Debug|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|Any CPU.Build.0 = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|ARM.ActiveCfg = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|ARM.Build.0 = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|iPhone.ActiveCfg = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|iPhone.Build.0 = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|x64.ActiveCfg = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|x64.Build.0 = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|x86.ActiveCfg = Release|Any CPU + {9D9CE4E4-1DD0-4961-861F-219731DE06CE}.Release|x86.Build.0 = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|ARM.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|iPhone.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|x64.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|x64.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|x86.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.AppStore|x86.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|ARM.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|ARM.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|iPhone.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|x64.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|x64.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|x86.ActiveCfg = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Debug|x86.Build.0 = Debug|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|Any CPU.Build.0 = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|ARM.ActiveCfg = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|ARM.Build.0 = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|iPhone.ActiveCfg = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|iPhone.Build.0 = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|x64.ActiveCfg = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|x64.Build.0 = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|x86.ActiveCfg = Release|Any CPU + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0}.Release|x86.Build.0 = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|ARM.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|iPhone.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|x64.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|x64.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|x86.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.AppStore|x86.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|ARM.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|ARM.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|iPhone.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|x64.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|x64.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|x86.ActiveCfg = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Debug|x86.Build.0 = Debug|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|Any CPU.Build.0 = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|ARM.ActiveCfg = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|ARM.Build.0 = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|iPhone.ActiveCfg = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|iPhone.Build.0 = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|x64.ActiveCfg = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|x64.Build.0 = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|x86.ActiveCfg = Release|Any CPU + {3F6202D0-2842-4C2F-98E1-9462709EAFBE}.Release|x86.Build.0 = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|ARM.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|iPhone.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|x64.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|x64.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|x86.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.AppStore|x86.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|ARM.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|iPhone.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|x64.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|x64.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Debug|x86.Build.0 = Debug|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|Any CPU.Build.0 = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|ARM.ActiveCfg = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|ARM.Build.0 = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|iPhone.ActiveCfg = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|iPhone.Build.0 = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|x64.ActiveCfg = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|x64.Build.0 = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|x86.ActiveCfg = Release|Any CPU + {B1182FD9-C245-4018-8412-C66F290C7F4C}.Release|x86.Build.0 = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|ARM.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|iPhone.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|x64.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|x64.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|x86.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.AppStore|x86.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|ARM.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|ARM.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|iPhone.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|x64.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|x64.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|x86.ActiveCfg = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Debug|x86.Build.0 = Debug|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|Any CPU.Build.0 = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|ARM.ActiveCfg = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|ARM.Build.0 = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|iPhone.ActiveCfg = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|iPhone.Build.0 = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|x64.ActiveCfg = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|x64.Build.0 = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|x86.ActiveCfg = Release|Any CPU + {38107691-A437-461D-A85C-ACD3AC7ACFAB}.Release|x86.Build.0 = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|ARM.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|iPhone.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|x64.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|x64.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|x86.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.AppStore|x86.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|ARM.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|ARM.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|iPhone.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|x64.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|x64.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|x86.ActiveCfg = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Debug|x86.Build.0 = Debug|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|Any CPU.Build.0 = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|ARM.ActiveCfg = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|ARM.Build.0 = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|iPhone.ActiveCfg = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|iPhone.Build.0 = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|x64.ActiveCfg = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|x64.Build.0 = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|x86.ActiveCfg = Release|Any CPU + {16F463AA-9CF6-44DC-B18C-7310CCF663FF}.Release|x86.Build.0 = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|ARM.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|iPhone.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|x64.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|x64.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|x86.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.AppStore|x86.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|ARM.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|ARM.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|iPhone.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|x64.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|x64.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|x86.ActiveCfg = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Debug|x86.Build.0 = Debug|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|Any CPU.Build.0 = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|ARM.ActiveCfg = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|ARM.Build.0 = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|iPhone.ActiveCfg = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|iPhone.Build.0 = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|x64.ActiveCfg = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|x64.Build.0 = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|x86.ActiveCfg = Release|Any CPU + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30}.Release|x86.Build.0 = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|ARM.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|iPhone.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|x64.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|x64.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|x86.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.AppStore|x86.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|ARM.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|ARM.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|iPhone.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|x64.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|x64.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|x86.ActiveCfg = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Debug|x86.Build.0 = Debug|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|Any CPU.Build.0 = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|ARM.ActiveCfg = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|ARM.Build.0 = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|iPhone.ActiveCfg = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|iPhone.Build.0 = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|x64.ActiveCfg = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|x64.Build.0 = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|x86.ActiveCfg = Release|Any CPU + {94176D9B-9CAA-4762-8D12-1621E240EE34}.Release|x86.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|ARM.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x64.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x86.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|Any CPU.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|ARM.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|ARM.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhone.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x64.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x64.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x86.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x86.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|ARM.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|ARM.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhone.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x64.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x64.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x86.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x86.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|Any CPU.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|ARM.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|ARM.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhone.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhone.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x64.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x64.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x86.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x86.Build.0 = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|ARM.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|iPhone.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|x64.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|x64.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|x86.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.AppStore|x86.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|ARM.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|ARM.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|iPhone.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|x64.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|x64.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|x86.ActiveCfg = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Debug|x86.Build.0 = Debug|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|Any CPU.Build.0 = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|ARM.ActiveCfg = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|ARM.Build.0 = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|iPhone.ActiveCfg = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|iPhone.Build.0 = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|x64.ActiveCfg = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|x64.Build.0 = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|x86.ActiveCfg = Release|Any CPU + {639BB197-D112-47A7-A44A-471DDB0FA1AE}.Release|x86.Build.0 = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|ARM.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|iPhone.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|x64.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|x64.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|x86.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.AppStore|x86.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|ARM.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|ARM.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|iPhone.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|x64.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|x64.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|x86.ActiveCfg = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Debug|x86.Build.0 = Debug|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|Any CPU.Build.0 = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|ARM.ActiveCfg = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|ARM.Build.0 = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|iPhone.ActiveCfg = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|iPhone.Build.0 = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|x64.ActiveCfg = Release|Any CPU + {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 @@ -1552,30 +1762,19 @@ Global {F5598DCB-6DDE-4661-AD9D-A55612DA7E76} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} {F0333D8E-0B27-42B7-B2C6-78F3657624E2} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} {95F1F07C-4D92-4742-BD07-E5B805AAB651} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} - {7796F5D8-31FC-45A4-B673-19DE5BA194CF} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {A579E108-5445-403D-A407-339AC4D1611B} = {24CD3B53-141E-4A07-9B0D-796641E1CF78} {F16E3C6A-1C94-4EAB-BE91-099618060B68} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} - {5B810E3D-112E-4857-B197-F09D2FD41E27} = {EF0337F2-ED00-4643-89FD-EE10863F1870} - {CFE2FACB-4538-4B99-8A10-306F3882952D} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {DB0EFB20-B024-4E5E-A75C-52143C131D25} = {932D8224-11F6-4D07-B109-DA28AD288A63} {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} {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} - {022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} - {1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5} - {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} - {969E793C-C413-490E-9C9D-B2B46DA5AF32} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {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} @@ -1589,6 +1788,26 @@ Global {AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1} = {28C0F5C8-4849-4035-80AB-45639424E73F} {7D63ED4A-3EDA-4BBA-8BBA-F46BD6430931} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} {E1D2B260-4E7F-4A88-BC13-9910F7C44623} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} + {9D9CE4E4-1DD0-4961-861F-219731DE06CE} = {2751AC5C-D148-4D7A-AE8F-149B47C9A82D} + {791961C7-3F3E-434E-B2BA-B4D6B5E222B0} = {5FB21302-3973-4992-962A-6F87F5EC99FD} + {3F6202D0-2842-4C2F-98E1-9462709EAFBE} = {2751AC5C-D148-4D7A-AE8F-149B47C9A82D} + {B1182FD9-C245-4018-8412-C66F290C7F4C} = {120CABB3-0FEA-4B40-B4B5-2D3041798C80} + {38107691-A437-461D-A85C-ACD3AC7ACFAB} = {5FB21302-3973-4992-962A-6F87F5EC99FD} + {16F463AA-9CF6-44DC-B18C-7310CCF663FF} = {C3F6ED48-E26D-4D57-970F-B82E69467BA1} + {DA7D3E03-D0B6-4591-8143-779D3E9F3F30} = {120CABB3-0FEA-4B40-B4B5-2D3041798C80} + {94176D9B-9CAA-4762-8D12-1621E240EE34} = {32EE4736-7534-47EC-BAAD-C00AF3130F80} + {969E793C-C413-490E-9C9D-B2B46DA5AF32} = {EF0337F2-ED00-4643-89FD-EE10863F1870} + {639BB197-D112-47A7-A44A-471DDB0FA1AE} = {EF0337F2-ED00-4643-89FD-EE10863F1870} + {2751AC5C-D148-4D7A-AE8F-149B47C9A82D} = {BF3EF4F3-E4F5-41DA-9D2D-57223687D1A8} + {5FB21302-3973-4992-962A-6F87F5EC99FD} = {326A7FB3-5295-468C-A4FE-67DCB823E1E5} + {C3F6ED48-E26D-4D57-970F-B82E69467BA1} = {41139F64-4046-4F16-96B7-D941D96FA9C6} + {32EE4736-7534-47EC-BAAD-C00AF3130F80} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} + {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/eShopOnContainers.sln b/eShopOnContainers.sln index 83125a051..8e08a05fa 100644 --- a/eShopOnContainers.sln +++ b/eShopOnContainers.sln @@ -25,7 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ordering", "Ordering", "{0B EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A857AD10-40FF-4303-BEC2-FF1C58D5735E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{EF0337F2-ED00-4643-89FD-EE10863F1870}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ServicesTests", "ServicesTests", "{EF0337F2-ED00-4643-89FD-EE10863F1870}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{24CD3B53-141E-4A07-9B0D-796641E1CF78}" EndProject @@ -41,16 +41,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebMVC", "src\Web\WebMVC\We EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.Infrastructure", "src\Services\Ordering\Ordering.Infrastructure\Ordering.Infrastructure.csproj", "{95F1F07C-4D92-4742-BD07-E5B805AAB651}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\UnitTest\UnitTest.csproj", "{7796F5D8-31FC-45A4-B673-19DE5BA194CF}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj", "{F16E3C6A-1C94-4EAB-BE91-099618060B68}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "test\Services\IntegrationTests\IntegrationTests.csproj", "{5B810E3D-112E-4857-B197-F09D2FD41E27}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\Services\FunctionalTests\FunctionalTests.csproj", "{CFE2FACB-4538-4B99-8A10-306F3882952D}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{DB0EFB20-B024-4E5E-A75C-52143C131D25}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventBus", "EventBus", "{807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}" @@ -61,20 +55,12 @@ 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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E145D-1593-47EE-9608-8E323D3C63F5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}" 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}" @@ -85,10 +71,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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoadTest", "test\Services\LoadTest\LoadTest.csproj", "{969E793C-C413-490E-9C9D-B2B46DA5AF32}" -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}" @@ -119,6 +101,66 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.TestRunne EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.BackgroundTasks", "src\Services\Ordering\Ordering.BackgroundTasks\Ordering.BackgroundTasks.csproj", "{16CDE5D2-2DDE-4AF2-B902-AD9CC42DE480}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basket.FunctionalTests", "src\Services\Basket\Basket.FunctionalTests\Basket.FunctionalTests.csproj", "{9F00E62F-E180-4A9C-8794-98A72AFAC2DB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basket.UnitTests", "src\Services\Basket\Basket.UnitTests\Basket.UnitTests.csproj", "{63417272-1E6A-406A-AD11-C738558D89C0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalog.FunctionalTests", "src\Services\Catalog\Catalog.FunctionalTests\Catalog.FunctionalTests.csproj", "{56E0E455-731E-41CB-AF46-C1A70F8A140B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalog.UnitTests", "src\Services\Catalog\Catalog.UnitTests\Catalog.UnitTests.csproj", "{1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Locations.FunctionalTests", "src\Services\Location\Locations.FunctionalTests\Locations.FunctionalTests.csproj", "{4F0E5CB2-5795-4040-8637-1D395914C944}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.FunctionalTests", "src\Services\Marketing\Marketing.FunctionalTests\Marketing.FunctionalTests.csproj", "{22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.FunctionalTests", "src\Services\Ordering\Ordering.FunctionalTests\Ordering.FunctionalTests.csproj", "{5A155B15-D8E7-47FE-8D17-8E641726158C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.UnitTests", "src\Services\Ordering\Ordering.UnitTests\Ordering.UnitTests.csproj", "{0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoadTest", "test\ServicesTests\LoadTest\LoadTest.csproj", "{969E793C-C413-490E-9C9D-B2B46DA5AF32}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application.FunctionalTests", "test\ServicesTests\Application.FunctionalTests\Application.FunctionalTests.csproj", "{3572B4E2-4399-4797-B5C2-3720D870E0C3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7BA332A2-189D-4D03-9935-FDFF81C42496}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2F0DEF71-84AC-4212-86D4-E36E8896BDBF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2B26A7AA-6D61-42FA-8AB7-C0F05AAE7F1C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{DA1786E4-30AB-434E-A827-92896390B79D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{30308DE0-8128-4613-BCAD-B0BEFFB20E38}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiGateways", "ApiGateways", "{79C64C7A-ED74-4F01-921F-92F4F9FC1E1D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiGw-Base", "ApiGw-Base", "{56AD1FCA-6E16-4798-BF29-941C5B3277D2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile.Bff.Marketing", "Mobile.Bff.Marketing", "{34ED3311-2B30-4C8B-823B-312B50FFC32A}" + ProjectSection(SolutionItems) = preProject + src\ApiGateways\Mobile.Bff.Marketing\apigw\configuration.json = src\ApiGateways\Mobile.Bff.Marketing\apigw\configuration.json + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile.Bff.Shopping", "Mobile.Bff.Shopping", "{A32A5254-BA36-46FC-8C75-F7B8FFE8FCD0}" + ProjectSection(SolutionItems) = preProject + src\ApiGateways\Mobile.Bff.Shopping\apigw\configuration.json = src\ApiGateways\Mobile.Bff.Shopping\apigw\configuration.json + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web.Bff.Marketing", "Web.Bff.Marketing", "{696D2B7E-6B75-401D-964A-BFE6F4D7AF73}" + ProjectSection(SolutionItems) = preProject + src\ApiGateways\Web.Bff.Marketing\apigw\configuration.json = src\ApiGateways\Web.Bff.Marketing\apigw\configuration.json + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web.Bff.Shopping", "Web.Bff.Shopping", "{424BC53E-17EA-4E12-BC07-64BAA927ABCB}" + ProjectSection(SolutionItems) = preProject + src\ApiGateways\Web.Bff.Shopping\apigw\configuration.json = src\ApiGateways\Web.Bff.Shopping\apigw\configuration.json + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotApiGw", "src\ApiGateways\ApiGw-Base\OcelotApiGw.csproj", "{0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mobile.Shopping.HttpAggregator", "src\ApiGateways\Mobile.Bff.Shopping\aggregator\Mobile.Shopping.HttpAggregator.csproj", "{98E0B3BA-6601-4C59-A9AA-24A00A17D835}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web.Shopping.HttpAggregator", "src\ApiGateways\Web.Bff.Shopping\aggregator\Web.Shopping.HttpAggregator.csproj", "{E39BD762-BC86-459D-B818-B6BF2D9F1352}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -373,54 +415,6 @@ Global {95F1F07C-4D92-4742-BD07-E5B805AAB651}.Release|x64.Build.0 = Release|Any CPU {95F1F07C-4D92-4742-BD07-E5B805AAB651}.Release|x86.ActiveCfg = Release|Any CPU {95F1F07C-4D92-4742-BD07-E5B805AAB651}.Release|x86.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|ARM.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhone.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x64.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x64.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x86.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x86.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|ARM.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|ARM.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhone.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x64.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x64.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x86.ActiveCfg = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x86.Build.0 = Debug|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|Any CPU.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|ARM.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|ARM.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhone.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhone.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x64.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x64.Build.0 = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x86.ActiveCfg = Release|Any CPU - {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x86.Build.0 = Release|Any CPU {A579E108-5445-403D-A407-339AC4D1611B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {A579E108-5445-403D-A407-339AC4D1611B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {A579E108-5445-403D-A407-339AC4D1611B}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -517,102 +511,6 @@ Global {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x64.Build.0 = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x86.ActiveCfg = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x86.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|ARM.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|iPhone.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|x64.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|x64.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|x86.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.AppStore|x86.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|ARM.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|ARM.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|iPhone.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|x64.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|x64.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|x86.ActiveCfg = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Debug|x86.Build.0 = Debug|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|Any CPU.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|ARM.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|ARM.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|iPhone.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|iPhone.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|x64.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|x64.Build.0 = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|x86.ActiveCfg = Release|Any CPU - {5B810E3D-112E-4857-B197-F09D2FD41E27}.Release|x86.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|ARM.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|iPhone.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|x64.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|x64.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|x86.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.AppStore|x86.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|ARM.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|ARM.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|iPhone.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|x64.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|x64.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|x86.ActiveCfg = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Debug|x86.Build.0 = Debug|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|Any CPU.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|ARM.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|ARM.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|iPhone.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|iPhone.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x64.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x64.Build.0 = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x86.ActiveCfg = Release|Any CPU - {CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x86.Build.0 = Release|Any CPU {0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -757,54 +655,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 @@ -901,102 +751,6 @@ Global {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.Build.0 = Release|Any CPU {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.ActiveCfg = Release|Any CPU {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.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 @@ -1141,102 +895,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 - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|ARM.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x64.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x86.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|Any CPU.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|ARM.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|ARM.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhone.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x64.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x64.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x86.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x86.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|Any CPU.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|ARM.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|ARM.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhone.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x64.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x64.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x86.ActiveCfg = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x86.Build.0 = Debug|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|Any CPU.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|Any CPU.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|ARM.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|ARM.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhone.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhone.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x64.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x64.Build.0 = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x86.ActiveCfg = Release|Any CPU - {969E793C-C413-490E-9C9D-B2B46DA5AF32}.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 @@ -1757,6 +1415,630 @@ Global {16CDE5D2-2DDE-4AF2-B902-AD9CC42DE480}.Release|x64.Build.0 = Release|Any CPU {16CDE5D2-2DDE-4AF2-B902-AD9CC42DE480}.Release|x86.ActiveCfg = Release|Any CPU {16CDE5D2-2DDE-4AF2-B902-AD9CC42DE480}.Release|x86.Build.0 = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|ARM.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|iPhone.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|x64.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|x64.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|x86.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.AppStore|x86.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|ARM.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|ARM.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|iPhone.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|x64.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|x64.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|x86.ActiveCfg = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Debug|x86.Build.0 = Debug|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|Any CPU.Build.0 = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|ARM.ActiveCfg = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|ARM.Build.0 = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|iPhone.ActiveCfg = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|iPhone.Build.0 = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|x64.ActiveCfg = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|x64.Build.0 = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|x86.ActiveCfg = Release|Any CPU + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB}.Release|x86.Build.0 = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|ARM.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|iPhone.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|x64.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|x64.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|x86.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.AppStore|x86.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|ARM.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|ARM.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|iPhone.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|x64.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|x64.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|x86.ActiveCfg = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Debug|x86.Build.0 = Debug|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|Any CPU.Build.0 = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|ARM.ActiveCfg = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|ARM.Build.0 = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|iPhone.ActiveCfg = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|iPhone.Build.0 = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|x64.ActiveCfg = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|x64.Build.0 = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|x86.ActiveCfg = Release|Any CPU + {63417272-1E6A-406A-AD11-C738558D89C0}.Release|x86.Build.0 = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|ARM.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|iPhone.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|x64.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|x64.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|x86.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.AppStore|x86.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|ARM.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|ARM.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|iPhone.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|x64.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|x64.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|x86.ActiveCfg = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Debug|x86.Build.0 = Debug|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|Any CPU.Build.0 = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|ARM.ActiveCfg = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|ARM.Build.0 = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|iPhone.ActiveCfg = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|iPhone.Build.0 = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|x64.ActiveCfg = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|x64.Build.0 = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|x86.ActiveCfg = Release|Any CPU + {56E0E455-731E-41CB-AF46-C1A70F8A140B}.Release|x86.Build.0 = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|ARM.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|iPhone.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|x64.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|x64.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|x86.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.AppStore|x86.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|ARM.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|ARM.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|iPhone.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|x64.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|x64.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|x86.ActiveCfg = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Debug|x86.Build.0 = Debug|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|Any CPU.Build.0 = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|ARM.ActiveCfg = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|ARM.Build.0 = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|iPhone.ActiveCfg = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|iPhone.Build.0 = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|x64.ActiveCfg = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|x64.Build.0 = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|x86.ActiveCfg = Release|Any CPU + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5}.Release|x86.Build.0 = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|ARM.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|iPhone.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|x64.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|x64.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|x86.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.AppStore|x86.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|ARM.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|ARM.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|iPhone.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|x64.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|x64.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|x86.ActiveCfg = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Debug|x86.Build.0 = Debug|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|Any CPU.Build.0 = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|ARM.ActiveCfg = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|ARM.Build.0 = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|iPhone.ActiveCfg = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|iPhone.Build.0 = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|x64.ActiveCfg = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|x64.Build.0 = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|x86.ActiveCfg = Release|Any CPU + {4F0E5CB2-5795-4040-8637-1D395914C944}.Release|x86.Build.0 = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|ARM.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|iPhone.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|x64.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|x64.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|x86.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.AppStore|x86.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|ARM.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|ARM.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|iPhone.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|x64.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|x64.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|x86.ActiveCfg = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Debug|x86.Build.0 = Debug|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|Any CPU.Build.0 = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|ARM.ActiveCfg = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|ARM.Build.0 = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|iPhone.ActiveCfg = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|iPhone.Build.0 = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|x64.ActiveCfg = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|x64.Build.0 = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|x86.ActiveCfg = Release|Any CPU + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8}.Release|x86.Build.0 = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|ARM.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|iPhone.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|x64.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|x64.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|x86.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.AppStore|x86.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|ARM.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|ARM.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|iPhone.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|x64.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Debug|x86.Build.0 = Debug|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|Any CPU.Build.0 = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|ARM.ActiveCfg = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|ARM.Build.0 = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|iPhone.ActiveCfg = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|iPhone.Build.0 = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|x64.ActiveCfg = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|x64.Build.0 = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|x86.ActiveCfg = Release|Any CPU + {5A155B15-D8E7-47FE-8D17-8E641726158C}.Release|x86.Build.0 = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|ARM.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|iPhone.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|x64.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|x64.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|x86.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.AppStore|x86.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|ARM.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|iPhone.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|x64.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|x64.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|x86.ActiveCfg = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Debug|x86.Build.0 = Debug|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|Any CPU.Build.0 = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|ARM.ActiveCfg = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|ARM.Build.0 = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|iPhone.ActiveCfg = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|iPhone.Build.0 = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|x64.ActiveCfg = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|x64.Build.0 = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|x86.ActiveCfg = Release|Any CPU + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A}.Release|x86.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|ARM.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x64.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Ad-Hoc|x86.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|Any CPU.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|ARM.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|ARM.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhone.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x64.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x64.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x86.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.AppStore|x86.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|ARM.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|ARM.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhone.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x64.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x64.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x86.ActiveCfg = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Debug|x86.Build.0 = Debug|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|Any CPU.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|ARM.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|ARM.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhone.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhone.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x64.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x64.Build.0 = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x86.ActiveCfg = Release|Any CPU + {969E793C-C413-490E-9C9D-B2B46DA5AF32}.Release|x86.Build.0 = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|ARM.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|iPhone.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|x64.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|x64.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|x86.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.AppStore|x86.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|ARM.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|ARM.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|iPhone.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|x64.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|x64.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|x86.ActiveCfg = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Debug|x86.Build.0 = Debug|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|Any CPU.Build.0 = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|ARM.ActiveCfg = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|ARM.Build.0 = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|iPhone.ActiveCfg = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|iPhone.Build.0 = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|x64.ActiveCfg = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|x64.Build.0 = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|x86.ActiveCfg = Release|Any CPU + {3572B4E2-4399-4797-B5C2-3720D870E0C3}.Release|x86.Build.0 = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|ARM.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|iPhone.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|x64.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|x64.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|x86.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.AppStore|x86.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|ARM.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|ARM.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|iPhone.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|x64.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|x64.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|x86.ActiveCfg = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Debug|x86.Build.0 = Debug|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|Any CPU.Build.0 = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|ARM.ActiveCfg = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|ARM.Build.0 = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|iPhone.ActiveCfg = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|iPhone.Build.0 = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|x64.ActiveCfg = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|x64.Build.0 = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|x86.ActiveCfg = Release|Any CPU + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC}.Release|x86.Build.0 = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|ARM.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|iPhone.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|x64.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|x64.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|x86.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.AppStore|x86.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|ARM.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|ARM.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|iPhone.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|x64.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|x64.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|x86.ActiveCfg = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Debug|x86.Build.0 = Debug|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|Any CPU.Build.0 = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|ARM.ActiveCfg = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|ARM.Build.0 = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|iPhone.ActiveCfg = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|iPhone.Build.0 = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|x64.ActiveCfg = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|x64.Build.0 = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|x86.ActiveCfg = Release|Any CPU + {98E0B3BA-6601-4C59-A9AA-24A00A17D835}.Release|x86.Build.0 = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|ARM.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|iPhone.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|x64.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|x64.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|x86.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.AppStore|x86.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|ARM.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|iPhone.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|x64.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|x64.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|x86.ActiveCfg = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Debug|x86.Build.0 = Debug|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|Any CPU.Build.0 = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|ARM.ActiveCfg = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|ARM.Build.0 = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|iPhone.ActiveCfg = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|iPhone.Build.0 = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|x64.ActiveCfg = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|x64.Build.0 = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|x86.ActiveCfg = Release|Any CPU + {E39BD762-BC86-459D-B818-B6BF2D9F1352}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1775,30 +2057,21 @@ Global {F5598DCB-6DDE-4661-AD9D-A55612DA7E76} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} {F0333D8E-0B27-42B7-B2C6-78F3657624E2} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} {95F1F07C-4D92-4742-BD07-E5B805AAB651} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} - {7796F5D8-31FC-45A4-B673-19DE5BA194CF} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {A579E108-5445-403D-A407-339AC4D1611B} = {24CD3B53-141E-4A07-9B0D-796641E1CF78} {F16E3C6A-1C94-4EAB-BE91-099618060B68} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} - {5B810E3D-112E-4857-B197-F09D2FD41E27} = {EF0337F2-ED00-4643-89FD-EE10863F1870} - {CFE2FACB-4538-4B99-8A10-306F3882952D} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {DB0EFB20-B024-4E5E-A75C-52143C131D25} = {932D8224-11F6-4D07-B109-DA28AD288A63} {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} {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} {022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} {1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5} - {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} - {969E793C-C413-490E-9C9D-B2B46DA5AF32} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {1815B651-941C-466B-AE33-D1D7EEB8F77F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} {15F4B3AA-89B6-4A0D-9051-414305974781} = {1815B651-941C-466B-AE33-D1D7EEB8F77F} {EF3EDC78-E864-43FF-8E80-CF33DD9508A3} = {932D8224-11F6-4D07-B109-DA28AD288A63} @@ -1814,6 +2087,30 @@ Global {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3} = {0AAED9FF-3260-43BB-B586-9AAF1E010A90} {A7337243-33B8-463A-87AD-944B75EFD820} = {0AAED9FF-3260-43BB-B586-9AAF1E010A90} {16CDE5D2-2DDE-4AF2-B902-AD9CC42DE480} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} + {9F00E62F-E180-4A9C-8794-98A72AFAC2DB} = {7BA332A2-189D-4D03-9935-FDFF81C42496} + {63417272-1E6A-406A-AD11-C738558D89C0} = {7BA332A2-189D-4D03-9935-FDFF81C42496} + {56E0E455-731E-41CB-AF46-C1A70F8A140B} = {2F0DEF71-84AC-4212-86D4-E36E8896BDBF} + {1EB6680D-4AC8-47EF-A026-E4EEEE93CAD5} = {2F0DEF71-84AC-4212-86D4-E36E8896BDBF} + {4F0E5CB2-5795-4040-8637-1D395914C944} = {2B26A7AA-6D61-42FA-8AB7-C0F05AAE7F1C} + {22F59DDE-D0BC-4D58-BCDE-240C7EEBECB8} = {DA1786E4-30AB-434E-A827-92896390B79D} + {5A155B15-D8E7-47FE-8D17-8E641726158C} = {30308DE0-8128-4613-BCAD-B0BEFFB20E38} + {0A9643F2-FF99-4DA0-BC2B-D62D5D3C317A} = {30308DE0-8128-4613-BCAD-B0BEFFB20E38} + {969E793C-C413-490E-9C9D-B2B46DA5AF32} = {EF0337F2-ED00-4643-89FD-EE10863F1870} + {3572B4E2-4399-4797-B5C2-3720D870E0C3} = {EF0337F2-ED00-4643-89FD-EE10863F1870} + {7BA332A2-189D-4D03-9935-FDFF81C42496} = {BF3EF4F3-E4F5-41DA-9D2D-57223687D1A8} + {2F0DEF71-84AC-4212-86D4-E36E8896BDBF} = {326A7FB3-5295-468C-A4FE-67DCB823E1E5} + {2B26A7AA-6D61-42FA-8AB7-C0F05AAE7F1C} = {41139F64-4046-4F16-96B7-D941D96FA9C6} + {DA1786E4-30AB-434E-A827-92896390B79D} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} + {30308DE0-8128-4613-BCAD-B0BEFFB20E38} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} + {79C64C7A-ED74-4F01-921F-92F4F9FC1E1D} = {932D8224-11F6-4D07-B109-DA28AD288A63} + {56AD1FCA-6E16-4798-BF29-941C5B3277D2} = {79C64C7A-ED74-4F01-921F-92F4F9FC1E1D} + {34ED3311-2B30-4C8B-823B-312B50FFC32A} = {79C64C7A-ED74-4F01-921F-92F4F9FC1E1D} + {A32A5254-BA36-46FC-8C75-F7B8FFE8FCD0} = {79C64C7A-ED74-4F01-921F-92F4F9FC1E1D} + {696D2B7E-6B75-401D-964A-BFE6F4D7AF73} = {79C64C7A-ED74-4F01-921F-92F4F9FC1E1D} + {424BC53E-17EA-4E12-BC07-64BAA927ABCB} = {79C64C7A-ED74-4F01-921F-92F4F9FC1E1D} + {0A328C44-4C4E-49BE-9FB4-9D851CEC28AC} = {56AD1FCA-6E16-4798-BF29-941C5B3277D2} + {98E0B3BA-6601-4C59-A9AA-24A00A17D835} = {A32A5254-BA36-46FC-8C75-F7B8FFE8FCD0} + {E39BD762-BC86-459D-B818-B6BF2D9F1352} = {424BC53E-17EA-4E12-BC07-64BAA927ABCB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9} diff --git a/hosts b/hosts deleted file mode 100644 index 19e812a15..000000000 --- a/hosts +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 1993-2009 Microsoft Corp. -# -# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. -# -# This file contains the mappings of IP addresses to host names. Each -# entry should be kept on an individual line. The IP address should -# be placed in the first column followed by the corresponding host name. -# The IP address and the host name should be separated by at least one -# space. -# -# Additionally, comments (such as these) may be inserted on individual -# lines or following the machine name denoted by a '#' symbol. -# -# For example: -# -# 102.54.94.97 rhino.acme.com # source server -# 38.25.63.10 x.acme.com # x client host - -# localhost name resolution is handled within DNS itself. -# 127.0.0.1 localhost -# ::1 localhost - - 10.0.75.1 identity.service \ No newline at end of file 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/aks-httpaddon-cfg.yaml b/k8s/helm/aks-httpaddon-cfg.yaml new file mode 100644 index 000000000..b9576b0b6 --- /dev/null +++ b/k8s/helm/aks-httpaddon-cfg.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: +# addonmanager.kubernetes.io/mode: Reconcile + app: addon-http-application-routing-ingress-nginx + kubernetes.io/cluster-service: "true" + name: addon-http-application-routing-nginx-configuration + namespace: kube-system +data: + proxy-buffer-size: "128k" + proxy-buffers: "4 256k" diff --git a/k8s/helm/apigwmm/.helmignore b/k8s/helm/apigwmm/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/apigwmm/.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/apigwmm/Chart.yaml b/k8s/helm/apigwmm/Chart.yaml new file mode 100644 index 000000000..50b3d07c6 --- /dev/null +++ b/k8s/helm/apigwmm/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: apigwmm +version: 0.1.0 diff --git a/k8s/helm/apigwmm/configuration-mobile-marketing.json b/k8s/helm/apigwmm/configuration-mobile-marketing.json new file mode 100644 index 000000000..666df1633 --- /dev/null +++ b/k8s/helm/apigwmm/configuration-mobile-marketing.json @@ -0,0 +1,34 @@ +{ + "ReRoutes": [ + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "marketing", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/m/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "locations", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/l/{everything}", + "UpstreamHttpMethod": [] + } + + ], + "GlobalConfiguration": { + "RequestIdKey": "OcRequestId", + "AdministrationPath": "/administration" + } +} + \ No newline at end of file diff --git a/k8s/helm/apigwmm/templates/NOTES.txt b/k8s/helm/apigwmm/templates/NOTES.txt new file mode 100644 index 000000000..30ef33447 --- /dev/null +++ b/k8s/helm/apigwmm/templates/NOTES.txt @@ -0,0 +1,2 @@ +eShop API Gateway for Mobile Marketing services installed +---------------------------------------------------------- \ No newline at end of file diff --git a/k8s/helm/apigwmm/templates/_helpers.tpl b/k8s/helm/apigwmm/templates/_helpers.tpl new file mode 100644 index 000000000..fd3d89212 --- /dev/null +++ b/k8s/helm/apigwmm/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "apigwmm.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 "apigwmm.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 "apigwmm.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/apigwmm/templates/_names.tpl b/k8s/helm/apigwmm/templates/_names.tpl new file mode 100644 index 000000000..a6eeb9965 --- /dev/null +++ b/k8s/helm/apigwmm/templates/_names.tpl @@ -0,0 +1,53 @@ +{{- 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" -}} +{{- $name := first .}} +{{- $ctx := last .}} +{{- if $ctx.Values.inf.k8s.suffix -}} +{{- $suffix := include "suffix-name" $ctx -}} +{{- printf "/%s-%s" $name $suffix -}} +{{- else -}} +{{- printf "/%s" $name -}} +{{- 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/apigwmm/templates/configmap.yaml b/k8s/helm/apigwmm/templates/configmap.yaml new file mode 100644 index 000000000..fbffcd339 --- /dev/null +++ b/k8s/helm/apigwmm/templates/configmap.yaml @@ -0,0 +1,21 @@ +{{- $name := include "apigwmm.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "apigwmm.name" . }} + chart: {{ template "apigwmm.chart" .}} + release: {{ .Release.Name }} + 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 new file mode 100644 index 000000000..6dc58bf50 --- /dev/null +++ b/k8s/helm/apigwmm/templates/deployment.yaml @@ -0,0 +1,103 @@ +{{- $name := include "apigwmm.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +{{- $ocelotcfgname := printf "%s-%s" "ocelot" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "apigwmm.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "apigwmm.name" . }} + chart: {{ template "apigwmm.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "apigwmm.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "apigwmm.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ $ocelotcfgname }} + items: + - key: configuration-mobile-marketing.json + 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: + - name: config + mountPath: /app/configuration + env: + - name: PATH_BASE + value: {{ include "pathBase" (list .Values.app.ingress.entries.mobilemarketingapigw .) }} + - 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/apigwmm/templates/ingress.yaml b/k8s/helm/apigwmm/templates/ingress.yaml new file mode 100644 index 000000000..28e2aa84d --- /dev/null +++ b/k8s/helm/apigwmm/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $ingressPath := include "pathBase" (list .Values.app.ingress.entries.mobilemarketingapigw .) -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "apigwmm.fullname" . }} + labels: + app: {{ template "apigwmm.name" . }} + chart: {{ template "apigwmm.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.mobilemarketingapigw }} + servicePort: http +{{- end }} diff --git a/k8s/helm/apigwmm/templates/ocelot-cm.yaml b/k8s/helm/apigwmm/templates/ocelot-cm.yaml new file mode 100644 index 000000000..c7c20ce06 --- /dev/null +++ b/k8s/helm/apigwmm/templates/ocelot-cm.yaml @@ -0,0 +1,14 @@ +{{- $name := include "apigwmm.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "ocelot-{{ $name }}" + labels: + app: {{ template "apigwmm.name" . }} + chart: {{ template "apigwmm.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + {{ (.Files.Glob "configuration-mobile-marketing.json").AsConfig | indent 2 }} + diff --git a/k8s/helm/apigwmm/templates/service.yaml b/k8s/helm/apigwmm/templates/service.yaml new file mode 100644 index 000000000..dac59b23a --- /dev/null +++ b/k8s/helm/apigwmm/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.mobilemarketingapigw }} + labels: + app: {{ template "apigwmm.name" . }} + chart: {{ template "apigwmm.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 "apigwmm.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/apigwmm/values.yaml b/k8s/helm/apigwmm/values.yaml new file mode 100644 index 000000000..501266780 --- /dev/null +++ b/k8s/helm/apigwmm/values.yaml @@ -0,0 +1,64 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /mobilemarketingapigw + +image: + repository: eshop/ocelotapigw + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + annotations: {} + 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: 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/.helmignore b/k8s/helm/apigwms/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/apigwms/.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/apigwms/Chart.yaml b/k8s/helm/apigwms/Chart.yaml new file mode 100644 index 000000000..3ad3fdf46 --- /dev/null +++ b/k8s/helm/apigwms/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: apigwms +version: 0.1.0 diff --git a/k8s/helm/apigwms/configuration-mobile-shopping.json b/k8s/helm/apigwms/configuration-mobile-shopping.json new file mode 100644 index 000000000..cf3a48aff --- /dev/null +++ b/k8s/helm/apigwms/configuration-mobile-shopping.json @@ -0,0 +1,142 @@ +{ + "ReRoutes": [ + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "catalog", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/c/{everything}", + "UpstreamHttpMethod": [ "GET" ] + }, + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "basket", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/b/{everything}", + "UpstreamHttpMethod": [], + "AuthenticationOptions": { + "AuthenticationProviderKey": "IdentityApiKey", + "AllowedScopes": [] + } + }, + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "ordering", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/o/{everything}", + "UpstreamHttpMethod": [], + "AuthenticationOptions": { + "AuthenticationProviderKey": "IdentityApiKey", + "AllowedScopes": [] + } + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "mobileshoppingagg", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/{everything}", + "UpstreamHttpMethod": [ "POST", "PUT", "GET" ], + "AuthenticationOptions": { + "AuthenticationProviderKey": "IdentityApiKey", + "AllowedScopes": [] + } + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "ordering", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/orders-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "basket", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/basket-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "catalog", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/catalog-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "marketing", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/marketing-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "payment", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/payment-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "locations.api", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/location-api/{everything}", + "UpstreamHttpMethod": [] + } + + ], + "GlobalConfiguration": { + "RequestIdKey": "OcRequestId", + "AdministrationPath": "/administration" + } + } + \ No newline at end of file diff --git a/k8s/helm/apigwms/templates/NOTES.txt b/k8s/helm/apigwms/templates/NOTES.txt new file mode 100644 index 000000000..74b3eedda --- /dev/null +++ b/k8s/helm/apigwms/templates/NOTES.txt @@ -0,0 +1,2 @@ +eShop API Gateway for Mobile Shopping services installed +-------------------------------------------------------- \ No newline at end of file diff --git a/k8s/helm/apigwms/templates/_helpers.tpl b/k8s/helm/apigwms/templates/_helpers.tpl new file mode 100644 index 000000000..2ae403c2f --- /dev/null +++ b/k8s/helm/apigwms/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "apigwms.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 "apigwms.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 "apigwms.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/apigwms/templates/_names.tpl b/k8s/helm/apigwms/templates/_names.tpl new file mode 100644 index 000000000..1e840c56c --- /dev/null +++ b/k8s/helm/apigwms/templates/_names.tpl @@ -0,0 +1,54 @@ +{{- 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" -}} +{{- $name := first .}} +{{- $ctx := last .}} +{{- if $ctx.Values.inf.k8s.suffix -}} +{{- $suffix := include "suffix-name" $ctx -}} +{{- printf "/%s-%s" $name $suffix -}} +{{- else -}} +{{- printf "/%s" $name -}} +{{- 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/apigwms/templates/configmap.yaml b/k8s/helm/apigwms/templates/configmap.yaml new file mode 100644 index 000000000..f3292ce72 --- /dev/null +++ b/k8s/helm/apigwms/templates/configmap.yaml @@ -0,0 +1,21 @@ +{{- $name := include "apigwms.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "apigwms.name" . }} + chart: {{ template "apigwms.chart" .}} + release: {{ .Release.Name }} + 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 new file mode 100644 index 000000000..8a4fd8942 --- /dev/null +++ b/k8s/helm/apigwms/templates/deployment.yaml @@ -0,0 +1,103 @@ +{{- $name := include "apigwms.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +{{- $ocelotcfgname := printf "%s-%s" "ocelot" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "apigwms.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "apigwms.name" . }} + chart: {{ template "apigwms.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "apigwms.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "apigwms.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ $ocelotcfgname }} + items: + - key: configuration-mobile-shopping.json + 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: + - name: config + mountPath: /app/configuration + env: + - name: PATH_BASE + value: {{ include "pathBase" (list .Values.app.ingress.entries.mobileshoppingapigw .) }} + - 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/apigwms/templates/ingress.yaml b/k8s/helm/apigwms/templates/ingress.yaml new file mode 100644 index 000000000..7dd50d8dd --- /dev/null +++ b/k8s/helm/apigwms/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $ingressPath := include "pathBase" (list .Values.app.ingress.entries.mobileshoppingapigw .) -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "apigwms.fullname" . }} + labels: + app: {{ template "apigwms.name" . }} + chart: {{ template "apigwms.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.mobileshoppingapigw }} + servicePort: http +{{- end }} diff --git a/k8s/helm/apigwms/templates/ocelot-cm.yaml b/k8s/helm/apigwms/templates/ocelot-cm.yaml new file mode 100644 index 000000000..5f92ca409 --- /dev/null +++ b/k8s/helm/apigwms/templates/ocelot-cm.yaml @@ -0,0 +1,14 @@ +{{- $name := include "apigwms.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "ocelot-{{ $name }}" + labels: + app: {{ template "apigwms.name" . }} + chart: {{ template "apigwms.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + {{ (.Files.Glob "configuration-mobile-shopping.json").AsConfig | indent 2 }} + diff --git a/k8s/helm/apigwms/templates/service.yaml b/k8s/helm/apigwms/templates/service.yaml new file mode 100644 index 000000000..2a37d3c14 --- /dev/null +++ b/k8s/helm/apigwms/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.mobileshoppingapigw }} + labels: + app: {{ template "apigwms.name" . }} + chart: {{ template "apigwms.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 "apigwms.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/apigwms/values.yaml b/k8s/helm/apigwms/values.yaml new file mode 100644 index 000000000..58377ec5b --- /dev/null +++ b/k8s/helm/apigwms/values.yaml @@ -0,0 +1,64 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /mobileshoppingapigw + +image: + repository: eshop/ocelotapigw + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + annotations: {} + 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: 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/.helmignore b/k8s/helm/apigwwm/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/apigwwm/.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/apigwwm/Chart.yaml b/k8s/helm/apigwwm/Chart.yaml new file mode 100644 index 000000000..4c2082969 --- /dev/null +++ b/k8s/helm/apigwwm/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: apigwwm +version: 0.1.0 diff --git a/k8s/helm/apigwwm/configuration-web-marketing.json b/k8s/helm/apigwwm/configuration-web-marketing.json new file mode 100644 index 000000000..666df1633 --- /dev/null +++ b/k8s/helm/apigwwm/configuration-web-marketing.json @@ -0,0 +1,34 @@ +{ + "ReRoutes": [ + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "marketing", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/m/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "locations", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/l/{everything}", + "UpstreamHttpMethod": [] + } + + ], + "GlobalConfiguration": { + "RequestIdKey": "OcRequestId", + "AdministrationPath": "/administration" + } +} + \ No newline at end of file diff --git a/k8s/helm/apigwwm/templates/NOTES.txt b/k8s/helm/apigwwm/templates/NOTES.txt new file mode 100644 index 000000000..3420c97c8 --- /dev/null +++ b/k8s/helm/apigwwm/templates/NOTES.txt @@ -0,0 +1,2 @@ +eShop API Gateway for Web Marketing services installed +------------------------------------------------------ \ No newline at end of file diff --git a/k8s/helm/apigwwm/templates/_helpers.tpl b/k8s/helm/apigwwm/templates/_helpers.tpl new file mode 100644 index 000000000..194cf96ca --- /dev/null +++ b/k8s/helm/apigwwm/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "apigwwm.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 "apigwwm.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 "apigwwm.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/apigwwm/templates/_names.tpl b/k8s/helm/apigwwm/templates/_names.tpl new file mode 100644 index 000000000..1e840c56c --- /dev/null +++ b/k8s/helm/apigwwm/templates/_names.tpl @@ -0,0 +1,54 @@ +{{- 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" -}} +{{- $name := first .}} +{{- $ctx := last .}} +{{- if $ctx.Values.inf.k8s.suffix -}} +{{- $suffix := include "suffix-name" $ctx -}} +{{- printf "/%s-%s" $name $suffix -}} +{{- else -}} +{{- printf "/%s" $name -}} +{{- 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/apigwwm/templates/configmap.yaml b/k8s/helm/apigwwm/templates/configmap.yaml new file mode 100644 index 000000000..34c0e6979 --- /dev/null +++ b/k8s/helm/apigwwm/templates/configmap.yaml @@ -0,0 +1,21 @@ +{{- $name := include "apigwwm.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "apigwwm.name" . }} + chart: {{ template "apigwwm.chart" .}} + release: {{ .Release.Name }} + 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 new file mode 100644 index 000000000..5cbce9f22 --- /dev/null +++ b/k8s/helm/apigwwm/templates/deployment.yaml @@ -0,0 +1,103 @@ +{{- $name := include "apigwwm.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +{{- $ocelotcfgname := printf "%s-%s" "ocelot" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "apigwwm.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "apigwwm.name" . }} + chart: {{ template "apigwwm.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "apigwwm.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "apigwwm.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ $ocelotcfgname }} + items: + - key: configuration-web-marketing.json + 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: + - name: config + mountPath: /app/configuration + env: + - name: PATH_BASE + value: {{ include "pathBase" (list .Values.app.ingress.entries.webmarketingapigw .) }} + - 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/apigwwm/templates/ingress.yaml b/k8s/helm/apigwwm/templates/ingress.yaml new file mode 100644 index 000000000..0a79c4660 --- /dev/null +++ b/k8s/helm/apigwwm/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $ingressPath := include "pathBase" (list .Values.app.ingress.entries.webmarketingapigw .) -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "apigwwm.fullname" . }} + labels: + app: {{ template "apigwwm.name" . }} + chart: {{ template "apigwwm.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.webmarketingapigw }} + servicePort: http +{{- end }} diff --git a/k8s/helm/apigwwm/templates/ocelot-cm.yaml b/k8s/helm/apigwwm/templates/ocelot-cm.yaml new file mode 100644 index 000000000..3de28b1a1 --- /dev/null +++ b/k8s/helm/apigwwm/templates/ocelot-cm.yaml @@ -0,0 +1,14 @@ +{{- $name := include "apigwwm.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "ocelot-{{ $name }}" + labels: + app: {{ template "apigwwm.name" . }} + chart: {{ template "apigwwm.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + {{ (.Files.Glob "configuration-web-marketing.json").AsConfig | indent 2 -}} + diff --git a/k8s/helm/apigwwm/templates/service.yaml b/k8s/helm/apigwwm/templates/service.yaml new file mode 100644 index 000000000..0ee3c4fb0 --- /dev/null +++ b/k8s/helm/apigwwm/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.webmarketingapigw }} + labels: + app: {{ template "apigwwm.name" . }} + chart: {{ template "apigwwm.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 "apigwwm.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/apigwwm/values.yaml b/k8s/helm/apigwwm/values.yaml new file mode 100644 index 000000000..68cbb89c4 --- /dev/null +++ b/k8s/helm/apigwwm/values.yaml @@ -0,0 +1,64 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /webmarketingapigw + +image: + repository: eshop/ocelotapigw + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + annotations: {} + 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: 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/.helmignore b/k8s/helm/apigwws/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/apigwws/.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/apigwws/Chart.yaml b/k8s/helm/apigwws/Chart.yaml new file mode 100644 index 000000000..0a6c34e62 --- /dev/null +++ b/k8s/helm/apigwws/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: apigwws +version: 0.1.0 diff --git a/k8s/helm/apigwws/configuration-web-shopping.json b/k8s/helm/apigwws/configuration-web-shopping.json new file mode 100644 index 000000000..021056f43 --- /dev/null +++ b/k8s/helm/apigwws/configuration-web-shopping.json @@ -0,0 +1,154 @@ +{ + "ReRoutes": [ + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "catalog", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/c/{everything}", + "UpstreamHttpMethod": [ "GET" ] + }, + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "basket", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/b/{everything}", + "UpstreamHttpMethod": [], + "AuthenticationOptions": { + "AuthenticationProviderKey": "IdentityApiKey", + "AllowedScopes": [] + } + }, + { + "DownstreamPathTemplate": "/api/{version}/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "ordering", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/api/{version}/o/{everything}", + "UpstreamHttpMethod": [], + "AuthenticationOptions": { + "AuthenticationProviderKey": "IdentityApiKey", + "AllowedScopes": [] + } + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "webshoppingagg", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/{everything}", + "UpstreamHttpMethod": [ "POST", "PUT", "GET" ], + "AuthenticationOptions": { + "AuthenticationProviderKey": "IdentityApiKey", + "AllowedScopes": [] + } + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "ordering", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/orders-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "ordering-signalrhub", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/hub/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "basket", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/basket-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "catalog", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/catalog-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "marketing", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/marketing-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "payment", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/payment-api/{everything}", + "UpstreamHttpMethod": [] + }, + { + "DownstreamPathTemplate": "/{everything}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "locations.api", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/location-api/{everything}", + "UpstreamHttpMethod": [] + } + + ], + "GlobalConfiguration": { + "RequestIdKey": "OcRequestId", + "AdministrationPath": "/administration" + } + } + \ No newline at end of file diff --git a/k8s/helm/apigwws/templates/NOTES.txt b/k8s/helm/apigwws/templates/NOTES.txt new file mode 100644 index 000000000..8214afb1e --- /dev/null +++ b/k8s/helm/apigwws/templates/NOTES.txt @@ -0,0 +1,2 @@ +eShop API Gateway for Web Shopping services installed +----------------------------------------------------- \ No newline at end of file diff --git a/k8s/helm/apigwws/templates/_helpers.tpl b/k8s/helm/apigwws/templates/_helpers.tpl new file mode 100644 index 000000000..b6aa6b483 --- /dev/null +++ b/k8s/helm/apigwws/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "apigwws.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 "apigwws.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 "apigwws.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/apigwws/templates/_names.tpl b/k8s/helm/apigwws/templates/_names.tpl new file mode 100644 index 000000000..a6eeb9965 --- /dev/null +++ b/k8s/helm/apigwws/templates/_names.tpl @@ -0,0 +1,53 @@ +{{- 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" -}} +{{- $name := first .}} +{{- $ctx := last .}} +{{- if $ctx.Values.inf.k8s.suffix -}} +{{- $suffix := include "suffix-name" $ctx -}} +{{- printf "/%s-%s" $name $suffix -}} +{{- else -}} +{{- printf "/%s" $name -}} +{{- 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/apigwws/templates/configmap.yaml b/k8s/helm/apigwws/templates/configmap.yaml new file mode 100644 index 000000000..dd5530f61 --- /dev/null +++ b/k8s/helm/apigwws/templates/configmap.yaml @@ -0,0 +1,21 @@ +{{- $name := include "apigwws.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "apigwws.name" . }} + chart: {{ template "apigwws.chart" .}} + release: {{ .Release.Name }} + 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 new file mode 100644 index 000000000..4912d12bb --- /dev/null +++ b/k8s/helm/apigwws/templates/deployment.yaml @@ -0,0 +1,102 @@ +{{- $name := include "apigwws.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +{{- $ocelotcfgname := printf "%s-%s" "ocelot" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "apigwws.fullname" . }} + labels: + app: {{ template "apigwws.name" . }} + chart: {{ template "apigwws.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "apigwws.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "apigwws.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ $ocelotcfgname }} + items: + - key: configuration-web-shopping.json + 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: + - name: config + mountPath: /app/configuration + env: + - name: PATH_BASE + value: {{ include "pathBase" (list .Values.app.ingress.entries.webshoppingapigw .) }} + - 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/apigwws/templates/ingress.yaml b/k8s/helm/apigwws/templates/ingress.yaml new file mode 100644 index 000000000..ee1f681ad --- /dev/null +++ b/k8s/helm/apigwws/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $ingressPath := include "pathBase" (list .Values.app.ingress.entries.webshoppingapigw .) -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "apigwws.fullname" . }} + labels: + app: {{ template "apigwws.name" . }} + chart: {{ template "apigwws.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.webshoppingapigw }} + servicePort: http +{{- end }} diff --git a/k8s/helm/apigwws/templates/ocelot-cm.yaml b/k8s/helm/apigwws/templates/ocelot-cm.yaml new file mode 100644 index 000000000..39b27f29a --- /dev/null +++ b/k8s/helm/apigwws/templates/ocelot-cm.yaml @@ -0,0 +1,14 @@ +{{- $name := include "apigwws.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "ocelot-{{ $name }}" + labels: + app: {{ template "apigwws.name" . }} + chart: {{ template "apigwws.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + {{ (.Files.Glob "configuration-web-shopping.json").AsConfig | indent 2 }} + diff --git a/k8s/helm/apigwws/templates/service.yaml b/k8s/helm/apigwws/templates/service.yaml new file mode 100644 index 000000000..5d74c2ad0 --- /dev/null +++ b/k8s/helm/apigwws/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.webshoppingapigw }} + labels: + app: {{ template "apigwws.name" . }} + chart: {{ template "apigwws.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 "apigwws.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/apigwws/values.yaml b/k8s/helm/apigwws/values.yaml new file mode 100644 index 000000000..94b8a203f --- /dev/null +++ b/k8s/helm/apigwws/values.yaml @@ -0,0 +1,64 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /webshoppingapigw + +image: + repository: eshop/ocelotapigw + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + annotations: {} + 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: 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 new file mode 100644 index 000000000..6b1c36922 --- /dev/null +++ b/k8s/helm/app.yaml @@ -0,0 +1,47 @@ +# This helm values file defines app-based settings +# Charts use those values, so this file **MUST** be included in all chart releases + + +app: # app global settings + name: "my-eshop" # Override for custom app name + ingress: # ingress related settings + entries: + basket: basket-api # ingress entry for basket api + catalog: catalog-api # ingress entry for catalog api + ordering: ordering-api # ingress entry for ordering api + identity: identity # ingress entry for identity api + mvc: webmvc # ingress entry for web mvc + spa: "" # ingress entry for web spa + status: webstatus # ingress entry for web status + webshoppingapigw: webshoppingapigw # ingress entry for web shopping Agw + webmarketingapigw: webmarketingapigw # ingress entry for web mkg Agw + mobilemarketingapigw: mobilemarketingapigw # ingress entry for mobile mkg Agw + mobileshoppingapigw: mobileshoppingapigw # ingress entry for mobile shopping Agw + webshoppingagg: webshoppingagg # ingress entry for web shopping aggregator + mobileshoppingagg: mobileshoppingagg # ingress entry for mobile shopping aggregator + 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: 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 + status: webstatus # service name for web status + webshoppingapigw: webshoppingapigw # service name for web shopping Agw + webmarketingapigw: webmarketingapigw # service name for web mkg Agw + mobilemarketingapigw: mobilemarketingapigw # service name for mobile mkg Agw + mobileshoppingapigw: mobileshoppingapigw # service name for mobile shopping Agw + webshoppingagg: webshoppingagg # service name for web shopping aggregator + mobileshoppingagg: mobileshoppingagg # service name for mobile shopping aggregator + 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/.helmignore b/k8s/helm/basket-api/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/basket-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/basket-api/Chart.yaml b/k8s/helm/basket-api/Chart.yaml new file mode 100644 index 000000000..fd3e01486 --- /dev/null +++ b/k8s/helm/basket-api/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: basket-api +version: 0.1.0 diff --git a/k8s/helm/basket-api/templates/NOTES.txt b/k8s/helm/basket-api/templates/NOTES.txt new file mode 100644 index 000000000..8ba2c89ee --- /dev/null +++ b/k8s/helm/basket-api/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop Basket 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 "basket-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 \ No newline at end of file diff --git a/k8s/helm/basket-api/templates/_helpers.tpl b/k8s/helm/basket-api/templates/_helpers.tpl new file mode 100644 index 000000000..550eb2e6c --- /dev/null +++ b/k8s/helm/basket-api/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "basket-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 "basket-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 "basket-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/basket-api/templates/_names.tpl b/k8s/helm/basket-api/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/basket-api/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/basket-api/templates/configmap.yaml b/k8s/helm/basket-api/templates/configmap.yaml new file mode 100644 index 000000000..c851de359 --- /dev/null +++ b/k8s/helm/basket-api/templates/configmap.yaml @@ -0,0 +1,19 @@ +{{- $name := include "basket-api.fullname" . -}} +{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "basket-api.name" . }} + chart: {{ template "basket-api.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + basket__ConnectionString: {{ .Values.inf.redis.basket.constr }} + urls__IdentityUrl: http://{{ $identity }} + basket__EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + 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/basket-api/templates/deployment.yaml b/k8s/helm/basket-api/templates/deployment.yaml new file mode 100644 index 000000000..d96c0cacf --- /dev/null +++ b/k8s/helm/basket-api/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "basket-api.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "basket-api.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "basket-api.name" . }} + chart: {{ template "basket-api.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "basket-api.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "basket-api.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/basket-api/templates/service.yaml b/k8s/helm/basket-api/templates/service.yaml new file mode 100644 index 000000000..20224c3b5 --- /dev/null +++ b/k8s/helm/basket-api/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.basket }} + labels: + app: {{ template "basket-api.name" . }} + chart: {{ template "basket-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 "basket-api.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/basket-api/values.yaml b/k8s/helm/basket-api/values.yaml new file mode 100644 index 000000000..6c264afba --- /dev/null +++ b/k8s/helm/basket-api/values.yaml @@ -0,0 +1,55 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /basket-api + +image: + repository: eshop/basket.api + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +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: basket__ConnectionString + - name: EventBusConnection + key: all__EventBusConnection + - name: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: AzureServiceBusEnabled + key: all__UseAzureServiceBus + - name: IdentityUrl + key: urls__IdentityUrl + - name: UseLoadTest + key: basket__EnableLoadTest + # values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value) + 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/basket-data/.helmignore b/k8s/helm/basket-data/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/basket-data/.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/basket-data/Chart.yaml b/k8s/helm/basket-data/Chart.yaml new file mode 100644 index 000000000..67ceddee1 --- /dev/null +++ b/k8s/helm/basket-data/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: basket-data +version: 0.1.0 diff --git a/k8s/helm/basket-data/templates/NOTES.txt b/k8s/helm/basket-data/templates/NOTES.txt new file mode 100644 index 000000000..c10513333 --- /dev/null +++ b/k8s/helm/basket-data/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop Redis for keystore data installed +---------------------------------------- + +Redis is not directly exposed outside cluster. If need to access it from outside use: + +export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "basket-data.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 \ No newline at end of file diff --git a/k8s/helm/basket-data/templates/_helpers.tpl b/k8s/helm/basket-data/templates/_helpers.tpl new file mode 100644 index 000000000..74b51b089 --- /dev/null +++ b/k8s/helm/basket-data/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "basket-data.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 "basket-data.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 "basket-data.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/basket-data/templates/deployment.yaml b/k8s/helm/basket-data/templates/deployment.yaml new file mode 100644 index 000000000..8ccceceeb --- /dev/null +++ b/k8s/helm/basket-data/templates/deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "basket-data.fullname" . }} + labels: + app: {{ template "basket-data.name" . }} + chart: {{ template "basket-data.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "basket-data.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "basket-data.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 6379 + 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/basket-data/templates/service.yaml b/k8s/helm/basket-data/templates/service.yaml new file mode 100644 index 000000000..98b8cc3bd --- /dev/null +++ b/k8s/helm/basket-data/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.inf.redis.basket.svc }} + labels: + app: {{ template "basket-data.name" . }} + chart: {{ template "basket-data.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 "basket-data.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/basket-data/values.yaml b/k8s/helm/basket-data/values.yaml new file mode 100644 index 000000000..17cc75ee7 --- /dev/null +++ b/k8s/helm/basket-data/values.yaml @@ -0,0 +1,19 @@ +replicaCount: 1 + +image: + repository: redis + tag: 4.0.10 + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 6379 + + +resources: {} + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/k8s/helm/catalog-api/.helmignore b/k8s/helm/catalog-api/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/catalog-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/catalog-api/Chart.yaml b/k8s/helm/catalog-api/Chart.yaml new file mode 100644 index 000000000..a143a0afe --- /dev/null +++ b/k8s/helm/catalog-api/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: catalog-api +version: 0.1.0 diff --git a/k8s/helm/catalog-api/templates/NOTES.txt b/k8s/helm/catalog-api/templates/NOTES.txt new file mode 100644 index 000000000..1f01a2b92 --- /dev/null +++ b/k8s/helm/catalog-api/templates/NOTES.txt @@ -0,0 +1,9 @@ +eShop Catalog 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 "catalog-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/catalog-api/templates/_helpers.tpl b/k8s/helm/catalog-api/templates/_helpers.tpl new file mode 100644 index 000000000..6fd128e77 --- /dev/null +++ b/k8s/helm/catalog-api/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "catalog-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 "catalog-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 "catalog-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/catalog-api/templates/_names.tpl b/k8s/helm/catalog-api/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/catalog-api/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/catalog-api/templates/configmap.yaml b/k8s/helm/catalog-api/templates/configmap.yaml new file mode 100644 index 000000000..95d8f2f9b --- /dev/null +++ b/k8s/helm/catalog-api/templates/configmap.yaml @@ -0,0 +1,20 @@ +{{- $name := include "catalog-api.fullname" . -}} +{{- $sqlsrv := include "sql-name" . -}} +{{- $webshoppingapigw := include "url-of" (list .Values.app.ingress.entries.webshoppingapigw .) -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "catalog-api.name" . }} + chart: {{ template "catalog-api.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + catalog__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.catalog.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }}; + catalog__PicBaseUrl: http://{{ $webshoppingapigw }}/api/v1/c/catalog/items/[0]/pic/ + catalog__AzureStorageEnabled: "{{ .Values.inf.misc.useAzureStorage }}" + 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/catalog-api/templates/deployment.yaml b/k8s/helm/catalog-api/templates/deployment.yaml new file mode 100644 index 000000000..d7a424a99 --- /dev/null +++ b/k8s/helm/catalog-api/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "catalog-api.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "catalog-api.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "catalog-api.name" . }} + chart: {{ template "catalog-api.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "catalog-api.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "catalog-api.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/catalog-api/templates/service.yaml b/k8s/helm/catalog-api/templates/service.yaml new file mode 100644 index 000000000..e63d4a4fc --- /dev/null +++ b/k8s/helm/catalog-api/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.catalog }} + labels: + app: {{ template "catalog-api.name" . }} + chart: {{ template "catalog-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 "catalog-api.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/catalog-api/values.yaml b/k8s/helm/catalog-api/values.yaml new file mode 100644 index 000000000..836db6125 --- /dev/null +++ b/k8s/helm/catalog-api/values.yaml @@ -0,0 +1,59 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /catalog-api + +image: + repository: eshop/catalog.api + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + + +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: catalog__ConnectionString + - name: PicBaseUrl + key: catalog__PicBaseUrl + - name: AzureStorageEnabled + key: catalog__AzureStorageEnabled + - name: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: EventBusConnection + key: all__EventBusConnection + - name: AzureServiceBusEnabled + key: all__UseAzureServiceBus + # 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' +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 new file mode 100644 index 000000000..1819c9a67 --- /dev/null +++ b/k8s/helm/deploy-all.ps1 @@ -0,0 +1,89 @@ +Param( + [parameter(Mandatory=$false)][string]$registry, + [parameter(Mandatory=$false)][string]$dockerUser, + [parameter(Mandatory=$false)][string]$dockerPassword, + [parameter(Mandatory=$false)][string]$externalDns, + [parameter(Mandatory=$false)][string]$appName="eshop", + [parameter(Mandatory=$false)][bool]$deployInfrastructure=$true, + [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)][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 + exit 1 + } + Write-Host "Getting DNS of AKS of AKS $aksName (in resource group $aksRg)..." -ForegroundColor Green + $dns = $(az aks show -n $aksName -g $aksRg --query addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName) + if ([string]::IsNullOrEmpty($dns)) { + Write-Host "Error getting DNS of AKS $aksName (in resource group $aksRg). Please ensure AKS has httpRouting enabled AND Azure CLI is logged & in version 2.0.37 or higher" -ForegroundColor Red + exit 1 + } + $dns = $dns -replace '[\"]' + Write-Host "DNS base found is $dns. Will use $appName.$dns for the app!" -ForegroundColor Green + $dns = "$appName.$dns" +} + +# Initialization & check commands +if ([string]::IsNullOrEmpty($dns)) { + Write-Host "No DNS specified. Ingress resources will be bound to public ip" -ForegroundColor Yellow +} + +if ($clean) { + Write-Host "Cleaning previous helm releases..." -ForegroundColor Green + helm delete --purge $(helm ls -q) + Write-Host "Previous releases deleted" -ForegroundColor Green +} + +$useCustomRegistry=$false + +if (-not [string]::IsNullOrEmpty($registry)) { + $useCustomRegistry=$true + if ([string]::IsNullOrEmpty($dockerUser) -or [string]::IsNullOrEmpty($dockerPassword)) { + Write-Host "Error: Must use -dockerUser AND -dockerPassword if specifying custom registry" -ForegroundColor Red + exit 1 + } +} + +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", "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 $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 $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 $ingressValuesFile --set app.name=$appName --set inf.k8s.dns=$dns --set image.tag=$imageTag --set image.pullPolicy=Always --name="$appName-$chart" $chart + } + } +} + +Write-Host "helm charts installed." -ForegroundColor Green + + + + diff --git a/k8s/helm/eshop-common/.helmignore b/k8s/helm/eshop-common/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/eshop-common/.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/eshop-common/Chart.yaml b/k8s/helm/eshop-common/Chart.yaml new file mode 100644 index 000000000..cd5e7b2fe --- /dev/null +++ b/k8s/helm/eshop-common/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: eshop-common +version: 0.1.0 diff --git a/k8s/helm/eshop-common/templates/NOTES.txt b/k8s/helm/eshop-common/templates/NOTES.txt new file mode 100644 index 000000000..1cc59f58a --- /dev/null +++ b/k8s/helm/eshop-common/templates/NOTES.txt @@ -0,0 +1,7 @@ +Common eShop resources installed: + +{{- if .Values.inf.registry -}} +* Docker registry secret ({{ .Values.inf.registry.secretName }}) +{{- end -}} + ++++ Done +++ \ No newline at end of file diff --git a/k8s/helm/eshop-common/templates/_helpers.tpl b/k8s/helm/eshop-common/templates/_helpers.tpl new file mode 100644 index 000000000..4a3c6324b --- /dev/null +++ b/k8s/helm/eshop-common/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "eshop-common.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 "eshop-common.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 "eshop-common.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/eshop-common/templates/_names.tpl b/k8s/helm/eshop-common/templates/_names.tpl new file mode 100644 index 000000000..7cdfb80d6 --- /dev/null +++ b/k8s/helm/eshop-common/templates/_names.tpl @@ -0,0 +1,3 @@ +{{- define "imagePullSecret" }} +{{- printf "{\"auths\": {\"%s\": {\"auth\": \"%s\"}}}" .Values.inf.registry.server (printf "%s:%s" .Values.inf.registry.login .Values.inf.registry.pwd | b64enc) | b64enc }} +{{- end }} \ No newline at end of file diff --git a/k8s/helm/eshop-common/templates/secret.yaml b/k8s/helm/eshop-common/templates/secret.yaml new file mode 100644 index 000000000..285ec85e7 --- /dev/null +++ b/k8s/helm/eshop-common/templates/secret.yaml @@ -0,0 +1,9 @@ +{{- if .Values.inf.registry -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.inf.registry.secretName }} +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ template "imagePullSecret" . }} +{{- end -}} \ No newline at end of file diff --git a/k8s/helm/eshop-common/values.yaml b/k8s/helm/eshop-common/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/k8s/helm/identity-api/.helmignore b/k8s/helm/identity-api/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/identity-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/identity-api/Chart.yaml b/k8s/helm/identity-api/Chart.yaml new file mode 100644 index 000000000..7b9290ada --- /dev/null +++ b/k8s/helm/identity-api/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: identity-api +version: 0.1.0 diff --git a/k8s/helm/identity-api/templates/NOTES.txt b/k8s/helm/identity-api/templates/NOTES.txt new file mode 100644 index 000000000..48fbbe9b4 --- /dev/null +++ b/k8s/helm/identity-api/templates/NOTES.txt @@ -0,0 +1,4 @@ +eShop Identity API installed. +----------------------------- + +Access this API through ingress. \ No newline at end of file diff --git a/k8s/helm/identity-api/templates/_helpers.tpl b/k8s/helm/identity-api/templates/_helpers.tpl new file mode 100644 index 000000000..fb47187b4 --- /dev/null +++ b/k8s/helm/identity-api/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "identity-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 "identity-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 "identity-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/identity-api/templates/_names.tpl b/k8s/helm/identity-api/templates/_names.tpl new file mode 100644 index 000000000..39ee485ef --- /dev/null +++ b/k8s/helm/identity-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/identity-api/templates/configmap.yaml b/k8s/helm/identity-api/templates/configmap.yaml new file mode 100644 index 000000000..24d71b699 --- /dev/null +++ b/k8s/helm/identity-api/templates/configmap.yaml @@ -0,0 +1,40 @@ +{{- $name := include "identity-api.fullname" . -}} +{{- $sqlsrv := include "sql-name" . -}} +{{- $mvc_url := include "url-of" (list .Values.app.ingress.entries.mvc .) -}} +{{- $spa_url := include "url-of" (list .Values.app.ingress.entries.spa .) -}} +{{- $locations_url := include "url-of" (list .Values.app.ingress.entries.locations .) -}} +{{- $marketing_url := include "url-of" (list .Values.app.ingress.entries.marketing .) -}} +{{- $basket_url := include "url-of" (list .Values.app.ingress.entries.basket .) -}} +{{- $ordering_url := include "url-of" (list .Values.app.ingress.entries.ordering .) -}} +{{- $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 +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "identity-api.name" . }} + chart: {{ template "identity-api.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + identity__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.identity.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }}; + identity__keystore: {{ .Values.inf.redis.keystore.constr }} + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + mvc_e: http://{{ $mvc_url }} + spa_e: http://{{ $spa_url }} + locations_e: http://{{ $locations_url }} + marketing_e: http://{{ $marketing_url }} + basket_e: http://{{ $basket_url }} + ordering_e: http://{{ $ordering_url }} + 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 new file mode 100644 index 000000000..0a4ee2722 --- /dev/null +++ b/k8s/helm/identity-api/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "identity-api.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "identity-api.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "identity-api.name" . }} + chart: {{ template "identity-api.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "identity-api.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "identity-api.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/identity-api/templates/ingress.yaml b/k8s/helm/identity-api/templates/ingress.yaml new file mode 100644 index 000000000..5824f91e2 --- /dev/null +++ b/k8s/helm/identity-api/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $ingressPath := include "pathBase" . -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "identity-api.fullname" . }} + labels: + app: {{ template "identity-api.name" . }} + chart: {{ template "identity-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.identity }} + servicePort: http +{{- end }} diff --git a/k8s/helm/identity-api/templates/service.yaml b/k8s/helm/identity-api/templates/service.yaml new file mode 100644 index 000000000..bca200389 --- /dev/null +++ b/k8s/helm/identity-api/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.identity }} + labels: + app: {{ template "identity-api.name" . }} + chart: {{ template "identity-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 "identity-api.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/identity-api/values.yaml b/k8s/helm/identity-api/values.yaml new file mode 100644 index 000000000..c0fd38192 --- /dev/null +++ b/k8s/helm/identity-api/values.yaml @@ -0,0 +1,79 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /identity + +image: + repository: eshop/identity.api + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + annotations: {} + hosts: + - chart-example.local + tls: [] + +resources: {} + + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +env: + urls: + configmap: + - name: ConnectionString + key: identity__ConnectionString + - name: DPConnectionString + key: identity__keystore + - name: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: MvcClient + key: mvc_e + - name: SpaClient + key: spa_e + - name: LocationApiClient + key: locations_e + - name: MarketingApiClient + key: marketing_e + - name: BasketApiClient + key: basket_e + - name: OrderingApiClient + key: ordering_e + - name: MobileShoppingAggClient + key: mobileshoppingagg_e + - name: WebShoppingAggClient + 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 + - name: OrchestratorType + 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 new file mode 100644 index 000000000..b6ff102b0 --- /dev/null +++ b/k8s/helm/inf.yaml @@ -0,0 +1,50 @@ +# This heml values file defines all infrastructure used by eShopOnContainers. +# It is used on all charts, so ** MUST BE INCLUDED ** on every deployment + +inf: + sql: # inf.sql defines the sql server databases & logins +# host: my-sql-server # Uncomment to specify a custom sql-server to be used. By default "sql-data-" will be used + common: + user: sa # SQL user + pwd: Pass@word # SQL pwd + pid: Developer + catalog: # inf.sql.catalog: settings for the catalog-api sql (user, pwd, db) + db: CatalogDb # Catalog API SQL db name + ordering: # inf.sql.ordering: settings for the ordering-api sql (user, pwd, db) + 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: + database: LocationsDb + marketing: + database: MarketingDb + redis: # inf.redis defines the redis' connection strings + basket: + svc: basket-data # Name of k8s svc for basket redis + constr: basket-data # Connection string to Redis used by Basket API + keystore: + svc: keystore-data # Name of k8s svc for keystore-data redis + constr: keystore-data # Connection string to Redis used as a Keystore (by Identity API) + eventbus: + svc: rabbitmq # Name of k8s svc for rabbitmq + constr: rabbitmq # Event bus connection string + useAzure: false # true if use Azure Service Bus. False if RabbitMQ + appinsights: + key: "" # App insights to use + k8s: # inf.k8s defines Kubernetes cluster global config + dns: "" # k8s external DNS. This value or ip value MUST BE PROVIDED + misc: # inf.misc contains miscellaneous configuration related to infrastructure + useLoadTest: false # If running under loading test or not + useAzureStorage: false # If catalog api uses azure storage or not +# registry: # Uncomment "registry" to specify registry secret +# secretName: # secretName is the name of the secret inside k8s +# server: # Registry login server +# login: # User login +# pwd: # User pwd + diff --git a/k8s/helm/ingress_values.yaml b/k8s/helm/ingress_values.yaml new file mode 100644 index 000000000..88540574d --- /dev/null +++ b/k8s/helm/ingress_values.yaml @@ -0,0 +1,5 @@ +ingress: + annotations: + kubernetes.io/ingress.class: addon-http-application-routing + ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/ssl-redirect: "false" 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/keystore-data/.helmignore b/k8s/helm/keystore-data/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/keystore-data/.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/keystore-data/Chart.yaml b/k8s/helm/keystore-data/Chart.yaml new file mode 100644 index 000000000..0cfa515f9 --- /dev/null +++ b/k8s/helm/keystore-data/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: keystore-data +version: 0.1.0 diff --git a/k8s/helm/keystore-data/templates/NOTES.txt b/k8s/helm/keystore-data/templates/NOTES.txt new file mode 100644 index 000000000..bec3a1f0f --- /dev/null +++ b/k8s/helm/keystore-data/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop Redis for keystore data installed +---------------------------------------- + +Redis is not directly exposed outside cluster. If need to access it from outside use: + +export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "keystore-data.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 \ No newline at end of file diff --git a/k8s/helm/keystore-data/templates/_helpers.tpl b/k8s/helm/keystore-data/templates/_helpers.tpl new file mode 100644 index 000000000..18786752f --- /dev/null +++ b/k8s/helm/keystore-data/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "keystore-data.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 "keystore-data.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 "keystore-data.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/keystore-data/templates/deployment.yaml b/k8s/helm/keystore-data/templates/deployment.yaml new file mode 100644 index 000000000..34f1fe074 --- /dev/null +++ b/k8s/helm/keystore-data/templates/deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "keystore-data.fullname" . }} + labels: + app: {{ template "keystore-data.name" . }} + chart: {{ template "keystore-data.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "keystore-data.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "keystore-data.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 6379 + 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/keystore-data/templates/service.yaml b/k8s/helm/keystore-data/templates/service.yaml new file mode 100644 index 000000000..38e9a4273 --- /dev/null +++ b/k8s/helm/keystore-data/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.inf.redis.keystore.svc }} + labels: + app: {{ template "keystore-data.name" . }} + chart: {{ template "keystore-data.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 "keystore-data.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/keystore-data/values.yaml b/k8s/helm/keystore-data/values.yaml new file mode 100644 index 000000000..17cc75ee7 --- /dev/null +++ b/k8s/helm/keystore-data/values.yaml @@ -0,0 +1,19 @@ +replicaCount: 1 + +image: + repository: redis + tag: 4.0.10 + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 6379 + + +resources: {} + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/k8s/helm/locations-api/.helmignore b/k8s/helm/locations-api/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/locations-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/locations-api/Chart.yaml b/k8s/helm/locations-api/Chart.yaml new file mode 100644 index 000000000..5126fe847 --- /dev/null +++ b/k8s/helm/locations-api/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: locations-api +version: 0.1.0 diff --git a/k8s/helm/locations-api/templates/NOTES.txt b/k8s/helm/locations-api/templates/NOTES.txt new file mode 100644 index 000000000..3b48889bf --- /dev/null +++ b/k8s/helm/locations-api/templates/NOTES.txt @@ -0,0 +1,9 @@ +eShop Locations 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 "locations-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/locations-api/templates/_helpers.tpl b/k8s/helm/locations-api/templates/_helpers.tpl new file mode 100644 index 000000000..086a461ba --- /dev/null +++ b/k8s/helm/locations-api/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "locations-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 "locations-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 "locations-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/locations-api/templates/_names.tpl b/k8s/helm/locations-api/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/locations-api/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/locations-api/templates/configmap.yaml b/k8s/helm/locations-api/templates/configmap.yaml new file mode 100644 index 000000000..84881087d --- /dev/null +++ b/k8s/helm/locations-api/templates/configmap.yaml @@ -0,0 +1,22 @@ +{{- $name := include "locations-api.fullname" . -}} +{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}} +{{- $mongo := include "mongo-name" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "locations-api.name" . }} + chart: {{ template "locations-api.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + all__EventBusConnection: {{ .Values.inf.eventbus.constr }} + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}" + all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + internalurls__IdentityUrl: http://{{ .Values.app.svc.identity }} + urls__IdentityUrl: {{ $identity }} + locations__ConnectionString: mongodb://{{ $mongo }} + locations__Database: {{ .Values.inf.mongo.locations.database }} \ No newline at end of file diff --git a/k8s/helm/locations-api/templates/deployment.yaml b/k8s/helm/locations-api/templates/deployment.yaml new file mode 100644 index 000000000..9667eb967 --- /dev/null +++ b/k8s/helm/locations-api/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "locations-api.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "locations-api.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "locations-api.name" . }} + chart: {{ template "locations-api.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "locations-api.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "locations-api.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/locations-api/templates/service.yaml b/k8s/helm/locations-api/templates/service.yaml new file mode 100644 index 000000000..abd628beb --- /dev/null +++ b/k8s/helm/locations-api/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.locations }} + labels: + app: {{ template "locations-api.name" . }} + chart: {{ template "locations-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 "locations-api.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/locations-api/values.yaml b/k8s/helm/locations-api/values.yaml new file mode 100644 index 000000000..4718f2a0b --- /dev/null +++ b/k8s/helm/locations-api/values.yaml @@ -0,0 +1,66 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /locations-api + +image: + repository: eshop/locations.api + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + 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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: EventBusConnection + key: all__EventBusConnection + - name: AzureServiceBusEnabled + key: all__UseAzureServiceBus + - name: UseLoadTest + key: all_EnableLoadTest + - name: IdentityUrl + key: internalurls__IdentityUrl + - name: IdentityUrlExternal + key: urls__IdentityUrl + - name: ConnectionString + key: locations__ConnectionString + - name: Database + key: locations__Database + # 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' +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/.helmignore b/k8s/helm/marketing-api/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/marketing-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/marketing-api/Chart.yaml b/k8s/helm/marketing-api/Chart.yaml new file mode 100644 index 000000000..173f94fd6 --- /dev/null +++ b/k8s/helm/marketing-api/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: marketing-api +version: 0.1.0 diff --git a/k8s/helm/marketing-api/templates/NOTES.txt b/k8s/helm/marketing-api/templates/NOTES.txt new file mode 100644 index 000000000..7fa66ed47 --- /dev/null +++ b/k8s/helm/marketing-api/templates/NOTES.txt @@ -0,0 +1,9 @@ +eShop Marketing 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 "marketing-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/marketing-api/templates/_helpers.tpl b/k8s/helm/marketing-api/templates/_helpers.tpl new file mode 100644 index 000000000..c252aeeac --- /dev/null +++ b/k8s/helm/marketing-api/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "marketing-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 "marketing-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 "marketing-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/marketing-api/templates/_names.tpl b/k8s/helm/marketing-api/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/marketing-api/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/marketing-api/templates/configmap.yaml b/k8s/helm/marketing-api/templates/configmap.yaml new file mode 100644 index 000000000..45f21e57d --- /dev/null +++ b/k8s/helm/marketing-api/templates/configmap.yaml @@ -0,0 +1,26 @@ +{{- $name := include "marketing-api.fullname" . -}} +{{- $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 +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "marketing-api.name" . }} + chart: {{ template "marketing-api.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + all__EventBusConnection: {{ .Values.inf.eventbus.constr }} + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}" + all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + internalurls__IdentityUrl: http://{{ .Values.app.svc.identity }} + urls__IdentityUrl: {{ $identity }} + 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 new file mode 100644 index 000000000..c49026c1b --- /dev/null +++ b/k8s/helm/marketing-api/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "marketing-api.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "marketing-api.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "marketing-api.name" . }} + chart: {{ template "marketing-api.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "marketing-api.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "marketing-api.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/marketing-api/templates/service.yaml b/k8s/helm/marketing-api/templates/service.yaml new file mode 100644 index 000000000..0e9bfbea2 --- /dev/null +++ b/k8s/helm/marketing-api/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.marketing }} + labels: + app: {{ template "marketing-api.name" . }} + chart: {{ template "marketing-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 "marketing-api.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/marketing-api/values.yaml b/k8s/helm/marketing-api/values.yaml new file mode 100644 index 000000000..00bebf27f --- /dev/null +++ b/k8s/helm/marketing-api/values.yaml @@ -0,0 +1,70 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /marketing-api + +image: + repository: eshop/marketing.api + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + 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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: EventBusConnection + key: all__EventBusConnection + - name: AzureServiceBusEnabled + key: all__UseAzureServiceBus + - name: UseLoadTest + key: all_EnableLoadTest + - name: IdentityUrl + key: internalurls__IdentityUrl + - name: IdentityUrlExternal + key: urls__IdentityUrl + - name: ConnectionString + key: marketing__ConnectionString + - 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) + values: + - name: ASPNETCORE_ENVIRONMENT + 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/.helmignore b/k8s/helm/mobileshoppingagg/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/mobileshoppingagg/.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/mobileshoppingagg/Chart.yaml b/k8s/helm/mobileshoppingagg/Chart.yaml new file mode 100644 index 000000000..957edd619 --- /dev/null +++ b/k8s/helm/mobileshoppingagg/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: mobileshoppingagg +version: 0.1.0 diff --git a/k8s/helm/mobileshoppingagg/templates/NOTES.txt b/k8s/helm/mobileshoppingagg/templates/NOTES.txt new file mode 100644 index 000000000..61971f717 --- /dev/null +++ b/k8s/helm/mobileshoppingagg/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop Mobile Marketing Aggregator is 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 "mobileshoppingagg.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/mobileshoppingagg/templates/_helpers.tpl b/k8s/helm/mobileshoppingagg/templates/_helpers.tpl new file mode 100644 index 000000000..b3aace0e7 --- /dev/null +++ b/k8s/helm/mobileshoppingagg/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "mobileshoppingagg.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 "mobileshoppingagg.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 "mobileshoppingagg.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/mobileshoppingagg/templates/_names.tpl b/k8s/helm/mobileshoppingagg/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/mobileshoppingagg/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/mobileshoppingagg/templates/configmap.yaml b/k8s/helm/mobileshoppingagg/templates/configmap.yaml new file mode 100644 index 000000000..d3e935409 --- /dev/null +++ b/k8s/helm/mobileshoppingagg/templates/configmap.yaml @@ -0,0 +1,26 @@ +{{- $name := include "mobileshoppingagg.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "mobileshoppingagg.name" . }} + chart: {{ template "mobileshoppingagg.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + mobileshoppingagg__keystore: {{ .Values.inf.redis.keystore.constr }} + internalurls__basket: http://{{ .Values.app.svc.basket }} + 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 new file mode 100644 index 000000000..0f23b3be6 --- /dev/null +++ b/k8s/helm/mobileshoppingagg/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "mobileshoppingagg.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "mobileshoppingagg.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "mobileshoppingagg.name" . }} + chart: {{ template "mobileshoppingagg.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "mobileshoppingagg.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "mobileshoppingagg.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/mobileshoppingagg/templates/service.yaml b/k8s/helm/mobileshoppingagg/templates/service.yaml new file mode 100644 index 000000000..ef6726e88 --- /dev/null +++ b/k8s/helm/mobileshoppingagg/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.mobileshoppingagg }} + labels: + app: {{ template "mobileshoppingagg.name" . }} + chart: {{ template "mobileshoppingagg.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 "mobileshoppingagg.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/mobileshoppingagg/values.yaml b/k8s/helm/mobileshoppingagg/values.yaml new file mode 100644 index 000000000..fd26c7794 --- /dev/null +++ b/k8s/helm/mobileshoppingagg/values.yaml @@ -0,0 +1,78 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /mobileshoppingagg + +image: + repository: eshop/mobileshoppingagg + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + 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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: urls__basket + key: internalurls__basket + - name: urls__catalog + key: internalurls__catalog + - name: urls__orders + 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 + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: OrchestratorType + 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/nosql-data/.helmignore b/k8s/helm/nosql-data/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/nosql-data/.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/nosql-data/Chart.yaml b/k8s/helm/nosql-data/Chart.yaml new file mode 100644 index 000000000..848a11cbb --- /dev/null +++ b/k8s/helm/nosql-data/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: nosql-data +version: 0.1.0 diff --git a/k8s/helm/nosql-data/templates/NOTES.txt b/k8s/helm/nosql-data/templates/NOTES.txt new file mode 100644 index 000000000..116c3c4e0 --- /dev/null +++ b/k8s/helm/nosql-data/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop MongoDb Installed +----------------------- + +MongoDb is not exposed outside the cluster. If need to access it from outside, use: + +export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "nosql-data.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 \ No newline at end of file diff --git a/k8s/helm/nosql-data/templates/_helpers.tpl b/k8s/helm/nosql-data/templates/_helpers.tpl new file mode 100644 index 000000000..99be734f7 --- /dev/null +++ b/k8s/helm/nosql-data/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "nosql-data.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 "nosql-data.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 "nosql-data.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/nosql-data/templates/_names.tpl b/k8s/helm/nosql-data/templates/_names.tpl new file mode 100644 index 000000000..56fb974fc --- /dev/null +++ b/k8s/helm/nosql-data/templates/_names.tpl @@ -0,0 +1,8 @@ + +{{- define "mongo-name" -}} +{{- if .Values.inf.mongo.host -}} +{{- .Values.inf.mongo.host -}} +{{- else -}} +{{- printf "%s" "nosql-data" -}} +{{- end -}} +{{- end -}} diff --git a/k8s/helm/nosql-data/templates/deployment.yaml b/k8s/helm/nosql-data/templates/deployment.yaml new file mode 100644 index 000000000..9b1f32319 --- /dev/null +++ b/k8s/helm/nosql-data/templates/deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "nosql-data.fullname" . }} + labels: + app: {{ template "nosql-data.name" . }} + chart: {{ template "nosql-data.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "nosql-data.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "nosql-data.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 27017 + 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/nosql-data/templates/service.yaml b/k8s/helm/nosql-data/templates/service.yaml new file mode 100644 index 000000000..478cadfea --- /dev/null +++ b/k8s/helm/nosql-data/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "mongo-name" . }} + labels: + app: {{ template "nosql-data.name" . }} + chart: {{ template "nosql-data.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 "nosql-data.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/nosql-data/values.yaml b/k8s/helm/nosql-data/values.yaml new file mode 100644 index 000000000..1a380e6b4 --- /dev/null +++ b/k8s/helm/nosql-data/values.yaml @@ -0,0 +1,19 @@ +replicaCount: 1 + +image: + repository: mongo + tag: 3.6.5-jessie + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 27017 + + +resources: {} + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/k8s/helm/ordering-api/.helmignore b/k8s/helm/ordering-api/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/ordering-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/ordering-api/Chart.yaml b/k8s/helm/ordering-api/Chart.yaml new file mode 100644 index 000000000..b65ca4b9a --- /dev/null +++ b/k8s/helm/ordering-api/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: ordering-api +version: 0.1.0 diff --git a/k8s/helm/ordering-api/templates/NOTES.txt b/k8s/helm/ordering-api/templates/NOTES.txt new file mode 100644 index 000000000..43bfd2fdf --- /dev/null +++ b/k8s/helm/ordering-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 "ordering-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/ordering-api/templates/_helpers.tpl b/k8s/helm/ordering-api/templates/_helpers.tpl new file mode 100644 index 000000000..978c08c64 --- /dev/null +++ b/k8s/helm/ordering-api/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "ordering-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 "ordering-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 "ordering-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/ordering-api/templates/_names.tpl b/k8s/helm/ordering-api/templates/_names.tpl new file mode 100644 index 000000000..39ee485ef --- /dev/null +++ b/k8s/helm/ordering-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/ordering-api/templates/configmap.yaml b/k8s/helm/ordering-api/templates/configmap.yaml new file mode 100644 index 000000000..de4cb2d7a --- /dev/null +++ b/k8s/helm/ordering-api/templates/configmap.yaml @@ -0,0 +1,20 @@ +{{- $name := include "ordering-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 "ordering-api.name" . }} + chart: {{ template "ordering-api.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + ordering__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.ordering.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }}; + ordering__EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + urls__IdentityUrl: 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/ordering-api/templates/deployment.yaml b/k8s/helm/ordering-api/templates/deployment.yaml new file mode 100644 index 000000000..a99ccdc28 --- /dev/null +++ b/k8s/helm/ordering-api/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "ordering-api.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "ordering-api.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "ordering-api.name" . }} + chart: {{ template "ordering-api.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "ordering-api.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "ordering-api.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/ordering-api/templates/service.yaml b/k8s/helm/ordering-api/templates/service.yaml new file mode 100644 index 000000000..bedfd6f01 --- /dev/null +++ b/k8s/helm/ordering-api/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.ordering }} + labels: + app: {{ template "ordering-api.name" . }} + chart: {{ template "ordering-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 "ordering-api.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/ordering-api/values.yaml b/k8s/helm/ordering-api/values.yaml new file mode 100644 index 000000000..c717d2793 --- /dev/null +++ b/k8s/helm/ordering-api/values.yaml @@ -0,0 +1,64 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /ordering-api + +image: + repository: eshop/ordering.api + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + 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: ordering__ConnectionString + - name: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: EventBusConnection + key: all__EventBusConnection + - name: AzureServiceBusEnabled + key: all__UseAzureServiceBus + - name: UseLoadTest + key: ordering__EnableLoadTest + - name: IdentityUrl + key: urls__IdentityUrl + # 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' +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/.helmignore b/k8s/helm/ordering-backgroundtasks/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/.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/ordering-backgroundtasks/Chart.yaml b/k8s/helm/ordering-backgroundtasks/Chart.yaml new file mode 100644 index 000000000..6ad4f47e6 --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: ordering-backgroundtasks +version: 0.1.0 diff --git a/k8s/helm/ordering-backgroundtasks/templates/NOTES.txt b/k8s/helm/ordering-backgroundtasks/templates/NOTES.txt new file mode 100644 index 000000000..54e1b49ea --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/templates/NOTES.txt @@ -0,0 +1,3 @@ +eShop Ordering Background Tasks installed. +------------------------------------------ + diff --git a/k8s/helm/ordering-backgroundtasks/templates/_helpers.tpl b/k8s/helm/ordering-backgroundtasks/templates/_helpers.tpl new file mode 100644 index 000000000..e61b78285 --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "ordering-backgroundtasks.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 "ordering-backgroundtasks.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 "ordering-backgroundtasks.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/ordering-backgroundtasks/templates/_names.tpl b/k8s/helm/ordering-backgroundtasks/templates/_names.tpl new file mode 100644 index 000000000..39ee485ef --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/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/ordering-backgroundtasks/templates/configmap.yaml b/k8s/helm/ordering-backgroundtasks/templates/configmap.yaml new file mode 100644 index 000000000..54fec785b --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/templates/configmap.yaml @@ -0,0 +1,20 @@ +{{- $name := include "ordering-backgroundtasks.fullname" . -}} +{{- $sqlsrv := include "sql-name" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "ordering-backgroundtasks.name" . }} + chart: {{ template "ordering-backgroundtasks.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + ordering__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.ordering.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }}; + ordering__EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + all__EventBusConnection: {{ .Values.inf.eventbus.constr }} + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}" + graceperiodmanager__CheckUpdateTime: "{{ .Values.cfg.checkUpdateTime }}" + graceperiodmanager__GracePeriodTime: "{{ .Values.cfg.gracePeriodTime }}" \ No newline at end of file diff --git a/k8s/helm/ordering-backgroundtasks/templates/deployment.yaml b/k8s/helm/ordering-backgroundtasks/templates/deployment.yaml new file mode 100644 index 000000000..017f9f3dd --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "ordering-backgroundtasks.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "ordering-backgroundtasks.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "ordering-backgroundtasks.name" . }} + chart: {{ template "ordering-backgroundtasks.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "ordering-backgroundtasks.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "ordering-backgroundtasks.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/ordering-backgroundtasks/templates/service.yaml b/k8s/helm/ordering-backgroundtasks/templates/service.yaml new file mode 100644 index 000000000..d8fcba036 --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.orderingbackgroundtasks }} + labels: + app: {{ template "ordering-backgroundtasks.name" . }} + chart: {{ template "ordering-backgroundtasks.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 "ordering-backgroundtasks.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/ordering-backgroundtasks/values.yaml b/k8s/helm/ordering-backgroundtasks/values.yaml new file mode 100644 index 000000000..d065f0345 --- /dev/null +++ b/k8s/helm/ordering-backgroundtasks/values.yaml @@ -0,0 +1,70 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /ordering-backgroundtasks + +image: + repository: eshop/ordering.backgroundtasks + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + hosts: + - chart-example.local + tls: [] + +cfg: + checkUpdateTime: "15000" + gracePeriodTime: "1" + +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: ordering__ConnectionString + - name: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: EventBusConnection + key: all__EventBusConnection + - name: AzureServiceBusEnabled + key: all__UseAzureServiceBus + - name: UseLoadTest + key: ordering__EnableLoadTest + - name: CheckUpdateTime + key: graceperiodmanager__CheckUpdateTime + - name: GracePeriodTime + key: graceperiodmanager__GracePeriodTime + # 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' +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/.helmignore b/k8s/helm/ordering-signalrhub/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/ordering-signalrhub/.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/ordering-signalrhub/Chart.yaml b/k8s/helm/ordering-signalrhub/Chart.yaml new file mode 100644 index 000000000..d43e83bf0 --- /dev/null +++ b/k8s/helm/ordering-signalrhub/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: ordering-signalrhub +version: 0.1.0 diff --git a/k8s/helm/ordering-signalrhub/templates/NOTES.txt b/k8s/helm/ordering-signalrhub/templates/NOTES.txt new file mode 100644 index 000000000..fc55c9dfa --- /dev/null +++ b/k8s/helm/ordering-signalrhub/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop Ordering SignalR Hub 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 "ordering-signalrhub.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/ordering-signalrhub/templates/_helpers.tpl b/k8s/helm/ordering-signalrhub/templates/_helpers.tpl new file mode 100644 index 000000000..2c11ddb51 --- /dev/null +++ b/k8s/helm/ordering-signalrhub/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "ordering-signalrhub.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 "ordering-signalrhub.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 "ordering-signalrhub.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/ordering-signalrhub/templates/_names.tpl b/k8s/helm/ordering-signalrhub/templates/_names.tpl new file mode 100644 index 000000000..39ee485ef --- /dev/null +++ b/k8s/helm/ordering-signalrhub/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/ordering-signalrhub/templates/configmap.yaml b/k8s/helm/ordering-signalrhub/templates/configmap.yaml new file mode 100644 index 000000000..addcf7e9d --- /dev/null +++ b/k8s/helm/ordering-signalrhub/templates/configmap.yaml @@ -0,0 +1,18 @@ +{{- $name := include "ordering-signalrhub.fullname" . -}} +{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "ordering-signalrhub.name" . }} + chart: {{ template "ordering-signalrhub.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + all__EventBusConnection: {{ .Values.inf.eventbus.constr }} + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}" + signalr__StoreConnectionString: {{ .Values.inf.redis.keystore.constr }} + urls__IdentityUrl: http://{{ $identity }} \ No newline at end of file diff --git a/k8s/helm/ordering-signalrhub/templates/deployment.yaml b/k8s/helm/ordering-signalrhub/templates/deployment.yaml new file mode 100644 index 000000000..af3867ea5 --- /dev/null +++ b/k8s/helm/ordering-signalrhub/templates/deployment.yaml @@ -0,0 +1,70 @@ +{{- $name := include "ordering-signalrhub.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "ordering-signalrhub.fullname" . }} + labels: + app: {{ template "ordering-signalrhub.name" . }} + chart: {{ template "ordering-signalrhub.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "ordering-signalrhub.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "ordering-signalrhub.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/ordering-signalrhub/templates/service.yaml b/k8s/helm/ordering-signalrhub/templates/service.yaml new file mode 100644 index 000000000..501539923 --- /dev/null +++ b/k8s/helm/ordering-signalrhub/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.orderingsignalrhub }} + labels: + app: {{ template "ordering-signalrhub.name" . }} + chart: {{ template "ordering-signalrhub.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 "ordering-signalrhub.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/ordering-signalrhub/values.yaml b/k8s/helm/ordering-signalrhub/values.yaml new file mode 100644 index 000000000..19099b147 --- /dev/null +++ b/k8s/helm/ordering-signalrhub/values.yaml @@ -0,0 +1,57 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /ordering-signalrhub + +image: + repository: eshop/ordering.signalrhub + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + hosts: + - chart-example.local + tls: [] + +cfg: + checkUpdateTime: "15000" + gracePeriodTime: "1" + +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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: EventBusConnection + key: all__EventBusConnection + - name: AzureServiceBusEnabled + key: all__UseAzureServiceBus + - name: IdentityUrl + key: urls__IdentityUrl + - name: SignalrStoreConnectionString + key: signalr__StoreConnectionString + # 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' + - name: IsClusterEnv + values: 'True' + diff --git a/k8s/helm/payment-api/.helmignore b/k8s/helm/payment-api/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/payment-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/payment-api/Chart.yaml b/k8s/helm/payment-api/Chart.yaml new file mode 100644 index 000000000..b7dba9341 --- /dev/null +++ b/k8s/helm/payment-api/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: payment-api +version: 0.1.0 diff --git a/k8s/helm/payment-api/templates/NOTES.txt b/k8s/helm/payment-api/templates/NOTES.txt new file mode 100644 index 000000000..6d178f344 --- /dev/null +++ b/k8s/helm/payment-api/templates/NOTES.txt @@ -0,0 +1,9 @@ +eShop Payment 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 "payment-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/payment-api/templates/_helpers.tpl b/k8s/helm/payment-api/templates/_helpers.tpl new file mode 100644 index 000000000..2f98d7ea2 --- /dev/null +++ b/k8s/helm/payment-api/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "payment-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 "payment-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 "payment-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/payment-api/templates/_names.tpl b/k8s/helm/payment-api/templates/_names.tpl new file mode 100644 index 000000000..39ee485ef --- /dev/null +++ b/k8s/helm/payment-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/payment-api/templates/configmap.yaml b/k8s/helm/payment-api/templates/configmap.yaml new file mode 100644 index 000000000..6717705b5 --- /dev/null +++ b/k8s/helm/payment-api/templates/configmap.yaml @@ -0,0 +1,15 @@ +{{- $name := include "payment-api.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "payment-api.name" . }} + chart: {{ template "payment-api.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + 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/payment-api/templates/deployment.yaml b/k8s/helm/payment-api/templates/deployment.yaml new file mode 100644 index 000000000..8b01f7394 --- /dev/null +++ b/k8s/helm/payment-api/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "payment-api.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "payment-api.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "payment-api.name" . }} + chart: {{ template "payment-api.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "payment-api.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "payment-api.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/payment-api/templates/service.yaml b/k8s/helm/payment-api/templates/service.yaml new file mode 100644 index 000000000..14fc7479c --- /dev/null +++ b/k8s/helm/payment-api/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.payment }} + labels: + app: {{ template "payment-api.name" . }} + chart: {{ template "payment-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 "payment-api.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/payment-api/values.yaml b/k8s/helm/payment-api/values.yaml new file mode 100644 index 000000000..341e4e1a9 --- /dev/null +++ b/k8s/helm/payment-api/values.yaml @@ -0,0 +1,56 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /payment-api + +image: + repository: eshop/payment.api + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + 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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: EventBusConnection + key: all__EventBusConnection + - name: AzureServiceBusEnabled + key: all__UseAzureServiceBus + # 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' +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/rabbitmq/.helmignore b/k8s/helm/rabbitmq/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/rabbitmq/.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/rabbitmq/Chart.yaml b/k8s/helm/rabbitmq/Chart.yaml new file mode 100644 index 000000000..2d955858e --- /dev/null +++ b/k8s/helm/rabbitmq/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: rabbitmq +version: 0.1.0 diff --git a/k8s/helm/rabbitmq/templates/NOTES.txt b/k8s/helm/rabbitmq/templates/NOTES.txt new file mode 100644 index 000000000..49edf7f9c --- /dev/null +++ b/k8s/helm/rabbitmq/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop rabbitmq installed +------------------------- + +rabbitmq is not directly exposed outside cluster. If need to access it from outside use: + +export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "rabbitmq.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 \ No newline at end of file diff --git a/k8s/helm/rabbitmq/templates/_helpers.tpl b/k8s/helm/rabbitmq/templates/_helpers.tpl new file mode 100644 index 000000000..bbbb2e33d --- /dev/null +++ b/k8s/helm/rabbitmq/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "rabbitmq.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 "rabbitmq.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 "rabbitmq.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/rabbitmq/templates/_names.tpl b/k8s/helm/rabbitmq/templates/_names.tpl new file mode 100644 index 000000000..be0a9b800 --- /dev/null +++ b/k8s/helm/rabbitmq/templates/_names.tpl @@ -0,0 +1,8 @@ + +{{- define "mongo-name" -}} +{{- if .Values.inf.mongo.host -}} +{{- .Values.inf.mongo.host -}} +{{- else -}} +{{- printf "%s" "rabbitmq" -}} +{{- end -}} +{{- end -}} diff --git a/k8s/helm/rabbitmq/templates/deployment.yaml b/k8s/helm/rabbitmq/templates/deployment.yaml new file mode 100644 index 000000000..9819a6455 --- /dev/null +++ b/k8s/helm/rabbitmq/templates/deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "rabbitmq.fullname" . }} + labels: + app: {{ template "rabbitmq.name" . }} + chart: {{ template "rabbitmq.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "rabbitmq.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "rabbitmq.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 5672 + 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/rabbitmq/templates/service.yaml b/k8s/helm/rabbitmq/templates/service.yaml new file mode 100644 index 000000000..5de39e0a8 --- /dev/null +++ b/k8s/helm/rabbitmq/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.inf.eventbus.svc }} + labels: + app: {{ template "rabbitmq.name" . }} + chart: {{ template "rabbitmq.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 "rabbitmq.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/rabbitmq/values.yaml b/k8s/helm/rabbitmq/values.yaml new file mode 100644 index 000000000..5e9efd521 --- /dev/null +++ b/k8s/helm/rabbitmq/values.yaml @@ -0,0 +1,19 @@ +replicaCount: 1 + +image: + repository: rabbitmq + tag: 3-management + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 5672 + + +resources: {} + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/k8s/helm/sql-data/.helmignore b/k8s/helm/sql-data/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/sql-data/.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/sql-data/Chart.yaml b/k8s/helm/sql-data/Chart.yaml new file mode 100644 index 000000000..6e5d726c5 --- /dev/null +++ b/k8s/helm/sql-data/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: sql-data +version: 0.1.0 diff --git a/k8s/helm/sql-data/templates/NOTES.txt b/k8s/helm/sql-data/templates/NOTES.txt new file mode 100644 index 000000000..468a155b0 --- /dev/null +++ b/k8s/helm/sql-data/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop SQL Server Installed +-------------------------- + +SQL server is not exposed outside the cluster. If need to access it from outside, use: + +export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "sql-data.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 \ No newline at end of file diff --git a/k8s/helm/sql-data/templates/_helpers.tpl b/k8s/helm/sql-data/templates/_helpers.tpl new file mode 100644 index 000000000..ee953f2f8 --- /dev/null +++ b/k8s/helm/sql-data/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "sql-data.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 "sql-data.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 "sql-data.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/sql-data/templates/_names.tpl b/k8s/helm/sql-data/templates/_names.tpl new file mode 100644 index 000000000..dc35d62fe --- /dev/null +++ b/k8s/helm/sql-data/templates/_names.tpl @@ -0,0 +1,8 @@ + +{{- define "sql-name" -}} +{{- if .Values.inf.sql.host -}} +{{- .Values.inf.sql.host -}} +{{- else -}} +{{- printf "%s" "sql-data" -}} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/k8s/helm/sql-data/templates/deployment.yaml b/k8s/helm/sql-data/templates/deployment.yaml new file mode 100644 index 000000000..4b2f589ef --- /dev/null +++ b/k8s/helm/sql-data/templates/deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "sql-data.fullname" . }} + labels: + app: {{ template "sql-data.name" . }} + chart: {{ template "sql-data.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "sql-data.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "sql-data.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: ACCEPT_EULA + value: "Y" + - name: MSSQL_PID + value: {{ .Values.inf.sql.common.pid }} + - name: MSSQL_SA_PASSWORD + value: {{ .Values.inf.sql.common.pwd }} + ports: + - name: http + containerPort: 1433 + 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/sql-data/templates/service.yaml b/k8s/helm/sql-data/templates/service.yaml new file mode 100644 index 000000000..b9b8d59fc --- /dev/null +++ b/k8s/helm/sql-data/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "sql-name" . }} + labels: + app: {{ template "sql-data.name" . }} + chart: {{ template "sql-data.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 "sql-data.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/sql-data/values.yaml b/k8s/helm/sql-data/values.yaml new file mode 100644 index 000000000..0ed76556a --- /dev/null +++ b/k8s/helm/sql-data/values.yaml @@ -0,0 +1,19 @@ +replicaCount: 1 + +image: + repository: microsoft/mssql-server-linux + tag: 2017-CU7 + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 1433 + + +resources: {} + +nodeSelector: {} + +tolerations: [] + +affinity: {} 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/.helmignore b/k8s/helm/webmvc/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/webmvc/.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/webmvc/Chart.yaml b/k8s/helm/webmvc/Chart.yaml new file mode 100644 index 000000000..c63e8924a --- /dev/null +++ b/k8s/helm/webmvc/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: webmvc +version: 0.1.0 diff --git a/k8s/helm/webmvc/templates/NOTES.txt b/k8s/helm/webmvc/templates/NOTES.txt new file mode 100644 index 000000000..06e02a45d --- /dev/null +++ b/k8s/helm/webmvc/templates/NOTES.txt @@ -0,0 +1,2 @@ +eShop WebMVC installed. +----------------------- diff --git a/k8s/helm/webmvc/templates/_helpers.tpl b/k8s/helm/webmvc/templates/_helpers.tpl new file mode 100644 index 000000000..2e3bcef56 --- /dev/null +++ b/k8s/helm/webmvc/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "webmvc.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 "webmvc.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 "webmvc.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/webmvc/templates/_names.tpl b/k8s/helm/webmvc/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/webmvc/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/webmvc/templates/configmap.yaml b/k8s/helm/webmvc/templates/configmap.yaml new file mode 100644 index 000000000..60dacdadd --- /dev/null +++ b/k8s/helm/webmvc/templates/configmap.yaml @@ -0,0 +1,29 @@ +{{- $name := include "webmvc.fullname" . -}} +{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}} +{{- $webshoppingapigw := include "url-of" (list .Values.app.ingress.entries.webshoppingapigw .) -}} +{{- $mvc := include "url-of" (list .Values.app.ingress.entries.mvc .) -}} +{{- $mongo := include "mongo-name" . -}} + + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "webmvc.name" . }} + chart: {{ template "webmvc.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}" + all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + webmvc__keystore: {{ .Values.inf.redis.keystore.constr }} + internalurls__apigwws: http://{{ .Values.app.svc.webshoppingapigw }} + internalurls__apigwwm: http://{{ .Values.app.svc.webmarketingapigw }} + internalurls__apigwws__hc: http://{{ .Values.app.svc.webshoppingapigw }}/hc + internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/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/templates/deployment.yaml b/k8s/helm/webmvc/templates/deployment.yaml new file mode 100644 index 000000000..d972db448 --- /dev/null +++ b/k8s/helm/webmvc/templates/deployment.yaml @@ -0,0 +1,71 @@ +{{- $name := include "webmvc.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "webmvc.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "webmvc.name" . }} + chart: {{ template "webmvc.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "webmvc.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "webmvc.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/webmvc/templates/ingress.yaml b/k8s/helm/webmvc/templates/ingress.yaml new file mode 100644 index 000000000..abfb62b2f --- /dev/null +++ b/k8s/helm/webmvc/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $ingressPath := include "pathBase" . -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "webmvc.fullname" . }} + labels: + app: {{ template "webmvc.name" . }} + chart: {{ template "webmvc.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.mvc }} + servicePort: http +{{- end }} diff --git a/k8s/helm/webmvc/templates/service.yaml b/k8s/helm/webmvc/templates/service.yaml new file mode 100644 index 000000000..74d87673f --- /dev/null +++ b/k8s/helm/webmvc/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.mvc }} + labels: + app: {{ template "webmvc.name" . }} + chart: {{ template "webmvc.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 "webmvc.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/webmvc/values.yaml b/k8s/helm/webmvc/values.yaml new file mode 100644 index 000000000..f4d077fc2 --- /dev/null +++ b/k8s/helm/webmvc/values.yaml @@ -0,0 +1,65 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /webmvc + +image: + repository: eshop/webmvc + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + annotations: {} + 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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: UseLoadTest + key: all_EnableLoadTest + - name: DPConnectionString + key: webmvc__keystore + - name: PurchaseUrl + key: internalurls__apigwws + - name: ExternalPurchaseUrl + key: urls__apigwws + - name: CallBackUrl + key: urls__mvc + - name: IdentityUrl + key: urls__IdentityUrl + - name: MarketingUrl + key: internalurls__apigwwm + - name: PurchaseUrlHC + key: internalurls__apigwws__hc + - name: IdentityUrlHC + key: internalurls__identity__hc + - name: MarketingUrlHC + 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) + values: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: OrchestratorType + value: 'K8S' + - name: IsClusterEnv + value: 'True' + diff --git a/k8s/helm/webshoppingagg/.helmignore b/k8s/helm/webshoppingagg/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/webshoppingagg/.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/webshoppingagg/Chart.yaml b/k8s/helm/webshoppingagg/Chart.yaml new file mode 100644 index 000000000..cd7541025 --- /dev/null +++ b/k8s/helm/webshoppingagg/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: webshoppingagg +version: 0.1.0 diff --git a/k8s/helm/webshoppingagg/templates/NOTES.txt b/k8s/helm/webshoppingagg/templates/NOTES.txt new file mode 100644 index 000000000..f55946f36 --- /dev/null +++ b/k8s/helm/webshoppingagg/templates/NOTES.txt @@ -0,0 +1,8 @@ +eShop Web Shopping Aggregator 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 "webshoppingagg.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/webshoppingagg/templates/_helpers.tpl b/k8s/helm/webshoppingagg/templates/_helpers.tpl new file mode 100644 index 000000000..f13dc791d --- /dev/null +++ b/k8s/helm/webshoppingagg/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "webshoppingagg.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 "webshoppingagg.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 "webshoppingagg.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/webshoppingagg/templates/_names.tpl b/k8s/helm/webshoppingagg/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/webshoppingagg/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/webshoppingagg/templates/configmap.yaml b/k8s/helm/webshoppingagg/templates/configmap.yaml new file mode 100644 index 000000000..b4d3da041 --- /dev/null +++ b/k8s/helm/webshoppingagg/templates/configmap.yaml @@ -0,0 +1,26 @@ +{{- $name := include "webshoppingagg.fullname" . -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "webshoppingagg.name" . }} + chart: {{ template "webshoppingagg.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + webshoppingagg__keystore: {{ .Values.inf.redis.keystore.constr }} + internalurls__basket: http://{{ .Values.app.svc.basket }} + 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 new file mode 100644 index 000000000..8007c74c8 --- /dev/null +++ b/k8s/helm/webshoppingagg/templates/deployment.yaml @@ -0,0 +1,92 @@ +{{- $name := include "webshoppingagg.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "webshoppingagg.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "webshoppingagg.name" . }} + chart: {{ template "webshoppingagg.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "webshoppingagg.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "webshoppingagg.name" . }} + release: {{ .Release.Name }} + spec: + {{ if .Values.inf.registry -}} + imagePullSecrets: + - name: {{ .Values.inf.registry.secretName }} + {{- 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: + - 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/webshoppingagg/templates/service.yaml b/k8s/helm/webshoppingagg/templates/service.yaml new file mode 100644 index 000000000..8f0cb8bd5 --- /dev/null +++ b/k8s/helm/webshoppingagg/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.webshoppingagg }} + labels: + app: {{ template "webshoppingagg.name" . }} + chart: {{ template "webshoppingagg.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 "webshoppingagg.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/webshoppingagg/values.yaml b/k8s/helm/webshoppingagg/values.yaml new file mode 100644 index 000000000..f4f2c5fd7 --- /dev/null +++ b/k8s/helm/webshoppingagg/values.yaml @@ -0,0 +1,79 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /webshoppingagg + +image: + repository: eshop/webshoppingagg + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + 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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: urls__basket + key: internalurls__basket + - name: urls__catalog + key: internalurls__catalog + - name: urls__orders + 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 + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: OrchestratorType + 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/.helmignore b/k8s/helm/webspa/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/webspa/.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/webspa/Chart.yaml b/k8s/helm/webspa/Chart.yaml new file mode 100644 index 000000000..c16616489 --- /dev/null +++ b/k8s/helm/webspa/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: webspa +version: 0.1.0 diff --git a/k8s/helm/webspa/templates/NOTES.txt b/k8s/helm/webspa/templates/NOTES.txt new file mode 100644 index 000000000..c8e1622db --- /dev/null +++ b/k8s/helm/webspa/templates/NOTES.txt @@ -0,0 +1,2 @@ +eShop WebSPA installed +---------------------- \ No newline at end of file diff --git a/k8s/helm/webspa/templates/_helpers.tpl b/k8s/helm/webspa/templates/_helpers.tpl new file mode 100644 index 000000000..585f9f001 --- /dev/null +++ b/k8s/helm/webspa/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "webspa.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 "webspa.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 "webspa.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/webspa/templates/_names.tpl b/k8s/helm/webspa/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/webspa/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/webspa/templates/configmap.yaml b/k8s/helm/webspa/templates/configmap.yaml new file mode 100644 index 000000000..7d4651a31 --- /dev/null +++ b/k8s/helm/webspa/templates/configmap.yaml @@ -0,0 +1,29 @@ +{{- $name := include "webspa.fullname" . -}} +{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}} +{{- $webshoppingapigw := include "url-of" (list .Values.app.ingress.entries.webshoppingapigw .) -}} +{{- $webmarketingapigw := include "url-of" (list .Values.app.ingress.entries.webmarketingapigw .) -}} +{{- $spa := include "url-of" (list .Values.app.ingress.entries.spa .) -}} +{{- $mongo := include "mongo-name" . -}} + + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "webspa.name" . }} + chart: {{ template "webspa.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + webspa__keystore: {{ .Values.inf.redis.keystore.constr }} + internalurls__apigwws: http://{{ .Values.app.svc.webshoppingapigw }} + internalurls__apigwws__hc: http://{{ .Values.app.svc.webshoppingapigw }}/hc + internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc + internalurls__apigwwm__hc: http://{{ .Values.app.svc.webmarketingapigw }}/hc + urls__apigwws: http://{{ $webshoppingapigw }} + urls__spa: http://{{ $spa }} + urls__IdentityUrl: http://{{ $identity }} + urls__apigwwm: http://{{ $webmarketingapigw }} \ No newline at end of file diff --git a/k8s/helm/webspa/templates/deployment.yaml b/k8s/helm/webspa/templates/deployment.yaml new file mode 100644 index 000000000..62af2d8b1 --- /dev/null +++ b/k8s/helm/webspa/templates/deployment.yaml @@ -0,0 +1,71 @@ +{{- $name := include "webspa.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "webspa.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "webspa.name" . }} + chart: {{ template "webspa.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "webspa.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "webspa.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/webspa/templates/ingress.yaml b/k8s/helm/webspa/templates/ingress.yaml new file mode 100644 index 000000000..85419f8e7 --- /dev/null +++ b/k8s/helm/webspa/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $ingressPath := include "pathBase" . -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "webspa.fullname" . }} + labels: + app: {{ template "webspa.name" . }} + chart: {{ template "webspa.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.spa }} + servicePort: http +{{- end }} diff --git a/k8s/helm/webspa/templates/service.yaml b/k8s/helm/webspa/templates/service.yaml new file mode 100644 index 000000000..2eab5d02e --- /dev/null +++ b/k8s/helm/webspa/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.spa }} + labels: + app: {{ template "webspa.name" . }} + chart: {{ template "webspa.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 "webspa.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/webspa/values.yaml b/k8s/helm/webspa/values.yaml new file mode 100644 index 000000000..056b58a0d --- /dev/null +++ b/k8s/helm/webspa/values.yaml @@ -0,0 +1,65 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: / + +image: + repository: eshop/webspa + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + annotations: {} + 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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: UseLoadTest + key: all_EnableLoadTest + - name: DPConnectionString + key: webspa__keystore + - name: PurchaseUrl + key: urls__apigwws + - name: CallBackUrl + key: urls__spa + - name: IdentityUrl + key: urls__IdentityUrl + - name: MarketingUrl + key: urls__apigwwm + - name: PurchaseUrlHC + key: internalurls__apigwws__hc + - name: IdentityUrlHC + key: internalurls__identity__hc + - name: MarketingUrlHC + 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) + values: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://0.0.0.0:80 + - name: OrchestratorType + value: 'K8S' + - name: IsClusterEnv + value: 'True' + diff --git a/k8s/helm/webstatus/.helmignore b/k8s/helm/webstatus/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/k8s/helm/webstatus/.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/webstatus/Chart.yaml b/k8s/helm/webstatus/Chart.yaml new file mode 100644 index 000000000..9ee2783f4 --- /dev/null +++ b/k8s/helm/webstatus/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: webstatus +version: 0.1.0 diff --git a/k8s/helm/webstatus/templates/NOTES.txt b/k8s/helm/webstatus/templates/NOTES.txt new file mode 100644 index 000000000..5d9d4570d --- /dev/null +++ b/k8s/helm/webstatus/templates/NOTES.txt @@ -0,0 +1,2 @@ +eShop WebStatus installed. +-------------------------- \ No newline at end of file diff --git a/k8s/helm/webstatus/templates/_helpers.tpl b/k8s/helm/webstatus/templates/_helpers.tpl new file mode 100644 index 000000000..65b290af7 --- /dev/null +++ b/k8s/helm/webstatus/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "webstatus.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 "webstatus.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 "webstatus.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/k8s/helm/webstatus/templates/_names.tpl b/k8s/helm/webstatus/templates/_names.tpl new file mode 100644 index 000000000..d44859fea --- /dev/null +++ b/k8s/helm/webstatus/templates/_names.tpl @@ -0,0 +1,52 @@ +{{- 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/webstatus/templates/configmap.yaml b/k8s/helm/webstatus/templates/configmap.yaml new file mode 100644 index 000000000..3f32d7adc --- /dev/null +++ b/k8s/helm/webstatus/templates/configmap.yaml @@ -0,0 +1,55 @@ +{{- $name := include "webstatus.fullname" . -}} +{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}} +{{- $webshoppingapigw := include "url-of" (list .Values.app.ingress.entries.webshoppingapigw .) -}} +{{- $mvc := include "url-of" (list .Values.app.ingress.entries.mvc .) -}} +{{- $mongo := include "mongo-name" . -}} + + +apiVersion: v1 +kind: ConfigMap +metadata: + name: "cfg-{{ $name }}" + labels: + app: {{ template "webstatus.name" . }} + chart: {{ template "webstatus.chart" .}} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + all__InstrumentationKey: {{ .Values.inf.appinsights.key }} + all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}" + all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" + webstatus__keystore: {{ .Values.inf.redis.keystore.constr }} + 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 + 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/templates/deployment.yaml b/k8s/helm/webstatus/templates/deployment.yaml new file mode 100644 index 000000000..6f96f81fb --- /dev/null +++ b/k8s/helm/webstatus/templates/deployment.yaml @@ -0,0 +1,71 @@ +{{- $name := include "webstatus.fullname" . -}} +{{- $cfgname := printf "%s-%s" "cfg" $name -}} +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "webstatus.fullname" . }} + labels: + ufo: {{ $cfgname}} + app: {{ template "webstatus.name" . }} + chart: {{ template "webstatus.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "webstatus.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "webstatus.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/webstatus/templates/ingress.yaml b/k8s/helm/webstatus/templates/ingress.yaml new file mode 100644 index 000000000..1ab4e5e22 --- /dev/null +++ b/k8s/helm/webstatus/templates/ingress.yaml @@ -0,0 +1,34 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "webstatus.fullname" . -}} +{{- $ingressPath := include "pathBase" . -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "webstatus.fullname" . }} + labels: + app: {{ template "webstatus.name" . }} + chart: {{ template "webstatus.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.status }} + servicePort: http +{{- end }} diff --git a/k8s/helm/webstatus/templates/service.yaml b/k8s/helm/webstatus/templates/service.yaml new file mode 100644 index 000000000..37fff50c6 --- /dev/null +++ b/k8s/helm/webstatus/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.app.svc.status }} + labels: + app: {{ template "webstatus.name" . }} + chart: {{ template "webstatus.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 "webstatus.name" . }} + release: {{ .Release.Name }} diff --git a/k8s/helm/webstatus/values.yaml b/k8s/helm/webstatus/values.yaml new file mode 100644 index 000000000..88cae5d7f --- /dev/null +++ b/k8s/helm/webstatus/values.yaml @@ -0,0 +1,110 @@ +replicaCount: 1 +clusterName: eshop-aks +pathBase: /webstatus + +image: + repository: eshop/webstatus + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + annotations: { + + } + 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: ApplicationInsights__InstrumentationKey + key: all__InstrumentationKey + - name: HealthChecks-UI__HealthChecks__0__Name + key: name__mvc__hc + - name: HealthChecks-UI__HealthChecks__0__Uri + key: internalurls__mvc__hc + - name: HealthChecks-UI__HealthChecks__1__Name + key: name__spa__hc + - name: HealthChecks-UI__HealthChecks__1__Uri + key: internalurls__spa__hc + - 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: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: OrchestratorType + value: 'K8S' 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 bbf3051aa..ed6d75229 100644 --- a/src/ApiGateways/ApiGw-Base/Dockerfile +++ b/src/ApiGateways/ApiGw-Base/Dockerfile @@ -1,13 +1,12 @@ -FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base WORKDIR /app EXPOSE 80 -FROM microsoft/dotnet:2.2-sdk AS build +FROM mcr.microsoft.com/dotnet/core/sdk:2.2 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 389e447dd..b1b6b1db6 100644 --- a/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj +++ b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj @@ -5,11 +5,13 @@ - - - - - - + + + + + + + + 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.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; +using Serilog; namespace OcelotApiGw { @@ -23,7 +24,20 @@ public static IWebHost BuildWebHost(string[] args) 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..467544051 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 @@ public void ConfigureServices(IServiceCollection services) 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 @@ public void ConfigureServices(IServiceCollection services) 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 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF 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 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 @@ public BasketController(ICatalogService catalogService, IBasketService basketSer [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 @@ public async Task UpdateAllBasket([FromBody] UpdateBasketRequest }); } - 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 @@ public async Task UpdateQuantities([FromBody] UpdateBasketItemsRe } // 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 @@ public async Task UpdateQuantities([FromBody] UpdateBasketItemsRe 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 @@ public async Task AddBasketItem([FromBody] AddBasketItemRequest d } // 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 @@ public async Task AddBasketItem([FromBody] AddBasketItemRequest d }); // 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 @@ public OrderController(IBasketService basketService, IOrderApiClient orderClient [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 eb095c437..d25a98e6c 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.2-aspnetcore-runtime AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base WORKDIR /app EXPOSE 80 -FROM microsoft/dotnet:2.2-sdk AS build +FROM mcr.microsoft.com/dotnet/core/sdk:2.2 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 98c89049e..fcb9fdf90 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj @@ -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.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Serilog; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator { @@ -31,6 +32,13 @@ public static IWebHost BuildWebHost(string[] args) => }); }) .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 @@ public BasketService(HttpClient httpClient, ILogger logger, IOpti _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 @@ public async Task GetById(string id) 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 @@ public CatalogService(HttpClient httpClient, ILogger logger, IOp _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 @@ public async Task GetCatalogItem(int id) 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 @@ public OrderApiClient(HttpClient httpClient, ILogger logger, IOp _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..8ac33593a 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 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 @@ public Startup(IConfiguration configuration) // 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,26 +57,42 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF 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(); } - - app.UseAuthentication(); - - 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"); - }); + else + { + app.UseHttpsRedirection(); + app.UseMvc(); + + app.UseSwagger().UseSwaggerUI(c => + { + c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); + + c.OAuthClientId("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui"); + c.OAuthClientSecret(string.Empty); + c.OAuthRealm(string.Empty); + c.OAuthAppName("Purchase BFF Swagger UI"); + }); + } } } @@ -73,7 +103,8 @@ public static IServiceCollection AddCustomMvc(this IServiceCollection services, services.AddOptions(); services.Configure(configuration.GetSection("urls")); - services.AddMvc(); + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddSwaggerGen(options => { @@ -104,9 +135,10 @@ public static IServiceCollection AddCustomMvc(this IServiceCollection services, services.AddCors(options => { options.AddPolicy("CorsPolicy", - builder => builder.AllowAnyOrigin() + builder => builder .AllowAnyMethod() .AllowAnyHeader() + .SetIsOriginAllowed((host) => true) .AllowCredentials()); }); @@ -121,7 +153,8 @@ public static IServiceCollection AddCustomAuthentication(this IServiceCollection options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }).AddJwtBearer(options => + }) + .AddJwtBearer(options => { options.Authority = identityUrl; options.RequireHttpsMetadata = false; @@ -130,17 +163,16 @@ public static IServiceCollection AddCustomAuthentication(this IServiceCollection { 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,24 +193,22 @@ public static IServiceCollection AddHttpServices(this IServiceCollection service .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() .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); } } -} +} \ No newline at end of file diff --git a/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json b/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json index 8d91e1402..ba71d26b3 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json +++ b/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json @@ -133,12 +133,10 @@ ], "UpstreamPathTemplate": "/image-tensorflow-api/{version}/{everything}", "UpstreamHttpMethod": [] - } ], - "GlobalConfiguration": { - "RequestIdKey": "OcRequestId", - "AdministrationPath": "/administration" - } - } - \ No newline at end of file + "GlobalConfiguration": { + "RequestIdKey": "OcRequestId", + "AdministrationPath": "/administration" + } +} \ No newline at end of file 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 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 @@ public BasketController(ICatalogService catalogService, IBasketService basketSer [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 @@ public async Task UpdateAllBasket([FromBody] UpdateBasketRequest }); } - 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 @@ public async Task UpdateQuantities([FromBody] UpdateBasketItemsRe } // 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 @@ public async Task UpdateQuantities([FromBody] UpdateBasketItemsRe } // 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 @@ public async Task AddBasketItem([FromBody] AddBasketItemRequest d } // 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 @@ public async Task AddBasketItem([FromBody] AddBasketItemRequest d }); // 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 @@ public OrderController(IBasketService basketService, IOrderApiClient orderClient [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 1836e5fed..75aa0f507 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.2-aspnetcore-runtime AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base WORKDIR /app EXPOSE 80 -FROM microsoft/dotnet:2.2-sdk AS build +FROM mcr.microsoft.com/dotnet/core/sdk:2.2 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.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Serilog; namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator { @@ -31,6 +32,13 @@ public static IWebHost BuildWebHost(string[] args) => }); }) .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 @@ public BasketService(HttpClient httpClient,ILogger logger, IOptio _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 @@ public CatalogService(HttpClient httpClient, ILogger logger, IOp _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 @@ public OrderApiClient(HttpClient httpClient, ILogger logger, IOp _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..a169b2fab 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.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 @@ public Startup(IConfiguration configuration) // 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 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF 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 @@ public static IServiceCollection AddCustomAuthentication(this IServiceCollection 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 @@ public static IServiceCollection AddCustomMvc(this IServiceCollection services, services.AddCors(options => { options.AddPolicy("CorsPolicy", - builder => builder.AllowAnyOrigin() + builder => builder + .SetIsOriginAllowed((host) => true) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); @@ -177,11 +209,13 @@ static IAsyncPolicy GetRetryPolicy() .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); } + static IAsyncPolicy GetCircuitBreakerPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); } + } } 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 db8b976ad..53a6db8f9 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj @@ -12,9 +12,13 @@ - - - + + + + + + + diff --git a/src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json b/src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json index bb4d80b9e..a175a4d60 100644 --- a/src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json +++ b/src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json @@ -96,7 +96,7 @@ "UpstreamPathTemplate": "/basket-api/{everything}", "UpstreamHttpMethod": [] }, - { + { "DownstreamPathTemplate": "/{everything}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ @@ -119,12 +119,7 @@ ], "UpstreamPathTemplate": "/payment-api/{everything}", "UpstreamHttpMethod": [] - }, - - // - // AI Gateway API routing - // - { + },{ "DownstreamPathTemplate": "/api/{version}/{everything}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ diff --git a/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEventHandler.cs b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEventHandler.cs index 0b5b793ee..72e1ed2cd 100644 --- a/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEventHandler.cs +++ b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEventHandler.cs @@ -6,11 +6,11 @@ namespace EventBus.Tests { - public class TestIntegrationOtherEventHandler : IIntegrationEventHandler + public class TestIntegrationEventHandler : IIntegrationEventHandler { public bool Handled { get; private set; } - public TestIntegrationOtherEventHandler() + public TestIntegrationEventHandler() { Handled = false; } diff --git a/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationOtherEventHandler.cs b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationOtherEventHandler.cs index 72e1ed2cd..0b5b793ee 100644 --- a/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationOtherEventHandler.cs +++ b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationOtherEventHandler.cs @@ -6,11 +6,11 @@ namespace EventBus.Tests { - public class TestIntegrationEventHandler : IIntegrationEventHandler + public class TestIntegrationOtherEventHandler : IIntegrationEventHandler { public bool Handled { get; private set; } - public TestIntegrationEventHandler() + public TestIntegrationOtherEventHandler() { Handled = false; } 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 @@ public IntegrationEvent() 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 @@ public void AddSubscription() 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/DefaultRabbitMQPersistentConnection.cs similarity index 91% rename from src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs rename to src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs index 2e0555e61..93e5b2917 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs @@ -72,7 +72,7 @@ public bool TryConnect() .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 @@ public bool TryConnect() _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 @@ public void Publish(IntegrationEvent @event) .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 @@ public void Publish(IntegrationEvent @event) 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 @@ public void Subscribe() { var eventName = _subsManager.GetEventKey(); DoInternalSubscription(eventName); + + _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName()); + _subsManager.AddSubscription(); } @@ -140,9 +146,13 @@ private void DoInternalSubscription(string eventName) } 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 @@ private async Task ProcessEvent(string eventName, string message) 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/IRabbitMQPersisterConnection.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs similarity index 100% rename from src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersisterConnection.cs rename to src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs index 446fcd7b7..cd2dc557f 100644 --- a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs +++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs @@ -22,15 +22,15 @@ public class EventBusServiceBus : IEventBus private readonly string AUTOFAC_SCOPE_NAME = "eshop_event_bus"; private const string INTEGRATION_EVENT_SUFIX = "IntegrationEvent"; - public EventBusServiceBus(IServiceBusPersisterConnection serviceBusPersisterConnection, + public EventBusServiceBus(IServiceBusPersisterConnection serviceBusPersisterConnection, ILogger logger, IEventBusSubscriptionsManager subsManager, string subscriptionClientName, ILifetimeScope autofac) { _serviceBusPersisterConnection = serviceBusPersisterConnection; - _logger = logger; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); - _subscriptionClient = new SubscriptionClient(serviceBusPersisterConnection.ServiceBusConnectionStringBuilder, + _subscriptionClient = new SubscriptionClient(serviceBusPersisterConnection.ServiceBusConnectionStringBuilder, subscriptionClientName); _autofac = autofac; @@ -61,6 +61,8 @@ public void Publish(IntegrationEvent @event) public void SubscribeDynamic(string eventName) where TH : IDynamicIntegrationEventHandler { + _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, nameof(TH)); + _subsManager.AddDynamicSubscription(eventName); } @@ -68,7 +70,7 @@ public void Subscribe() where T : IntegrationEvent where TH : IIntegrationEventHandler { - var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFIX, ""); + var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFIX, ""); var containsKey = _subsManager.HasSubscriptionsForEvent(); if (!containsKey) @@ -81,12 +83,14 @@ public void Subscribe() Name = eventName }).GetAwaiter().GetResult(); } - catch(ServiceBusException) + 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 @@ public void Unsubscribe() } 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); } @@ -129,27 +137,29 @@ private void RegisterSubscriptionClientMessageHandler() { var eventName = $"{message.Label}{INTEGRATION_EVENT_SUFIX}"; var messageData = Encoding.UTF8.GetString(message.Body); - await ProcessEvent(eventName, messageData); - + // Complete the message so that it is not received again. - await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken); + if (await ProcessEvent(eventName, messageData)) + { + 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; } - private async Task ProcessEvent(string eventName, string message) + private async Task ProcessEvent(string eventName, string message) { + var processed = false; if (_subsManager.HasSubscriptionsForEvent(eventName)) { using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME)) @@ -160,20 +170,24 @@ private async Task ProcessEvent(string eventName, string message) 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 }); } } } + processed = true; } + return processed; } private void RemoveDefaultRule() @@ -187,8 +201,8 @@ private void RemoveDefaultRule() } 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); } } } -} +} \ No newline at end of file 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/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.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 @@ public class IntegrationEventLogEntry 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 @@ public IntegrationEventLogEntry(IntegrationEvent @event) } 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 5ac8bb862..2712c5e1c 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs @@ -1,12 +1,15 @@ using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; +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; -using System; -using Microsoft.EntityFrameworkCore.Diagnostics; namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services { @@ -14,37 +17,69 @@ public class IntegrationEventLogService : IIntegrationEventLogService { private readonly IntegrationEventLogContext _integrationEventLogContext; private readonly DbConnection _dbConnection; + private readonly List _eventTypes; public IntegrationEventLogService(DbConnection dbConnection) { - _dbConnection = dbConnection?? throw new ArgumentNullException("dbConnection"); + _dbConnection = dbConnection ?? throw new ArgumentNullException(nameof(dbConnection)); _integrationEventLogContext = new IntegrationEventLogContext( new DbContextOptionsBuilder() .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) { - if(transaction == null) + if (transaction == null) { - throw new ArgumentNullException("transaction", $"A {typeof(DbTransaction).FullName} is required as a pre-requisite to save the event."); + throw new ArgumentNullException(nameof(transaction), $"A {typeof(DbTransaction).FullName} is required as a pre-requisite to save the event."); } - + var eventLogEntry = new IntegrationEventLogEntry(@event); - + _integrationEventLogContext.Database.UseTransaction(transaction); _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); 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.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/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/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/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/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 @@ public static IWebHost MigrateDbContext(this IWebHost webHost, Action< 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.Core/GlobalSettings.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs index 6d3b86757..61fc7da42 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs @@ -104,7 +104,10 @@ private void UpdateGatewayMarketingEndpoint(string endpoint) private string ExtractBaseUri(string endpoint) { - return Uri.TryCreate(endpoint, UriKind.Absolute, out Uri uri) ? uri.GetLeftPart(UriPartial.Authority) : String.Empty; + var uri = new Uri(endpoint); + var baseUri = uri.GetLeftPart(System.UriPartial.Authority); + + return baseUri; } } } \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj index 9c34eddbd..ce66d3e98 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj @@ -58,6 +58,9 @@ + + ..\..\..\..\packages\Xamarin.Forms.3.0.0.482510\lib\MonoAndroid10\FormsViewGroup.dll + diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/eShopOnContainers.TestRunner.Droid.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/eShopOnContainers.TestRunner.Droid.csproj index 787cb722c..4e1f592e4 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/eShopOnContainers.TestRunner.Droid.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.Droid/eShopOnContainers.TestRunner.Droid.csproj @@ -50,6 +50,9 @@ False + + ..\..\..\..\packages\Xamarin.Forms.3.0.0.482510\lib\MonoAndroid10\FormsViewGroup.dll + @@ -118,6 +121,18 @@ ..\..\..\..\packages\Xamarin.Android.Support.v7.MediaRouter.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.dll + + ..\..\..\..\packages\Xamarin.Forms.3.0.0.482510\lib\MonoAndroid10\Xamarin.Forms.Core.dll + + + ..\..\..\..\packages\Xamarin.Forms.3.0.0.482510\lib\MonoAndroid10\Xamarin.Forms.Platform.dll + + + ..\..\..\..\packages\Xamarin.Forms.3.0.0.482510\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll + + + ..\..\..\..\packages\Xamarin.Forms.3.0.0.482510\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll + ..\..\..\..\packages\xunit.runner.devices.2.3.3\lib\monoandroid80\xunit.runner.devices.dll diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj index 61c95b24e..36ca5f513 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj @@ -59,7 +59,7 @@ prompt 4 false - ARM64 + ARMv7, ARM64 Entitlements.plist iPhone Developer true 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/AI.SalesForecasting/AI.SalesForecasting.MLNet.API/Dockerfile b/src/Services/AI.SalesForecasting/AI.SalesForecasting.MLNet.API/Dockerfile index 54b2d79f8..eb5f42ef6 100644 --- a/src/Services/AI.SalesForecasting/AI.SalesForecasting.MLNet.API/Dockerfile +++ b/src/Services/AI.SalesForecasting/AI.SalesForecasting.MLNet.API/Dockerfile @@ -10,11 +10,11 @@ WORKDIR /src COPY . . WORKDIR /src/src/Services/AI.SalesForecasting/AI.SalesForecasting.MLNet.API RUN dotnet restore -nowarn:msb3202,nu1503 -RUN dotnet build -c ${configuration} -o /app +RUN dotnet build -c release -o /app FROM build AS publish ARG configuration -RUN dotnet publish --no-restore -c ${configuration} -o /app +RUN dotnet publish --no-restore -c release -o /app FROM base AS final WORKDIR /app diff --git a/src/Services/Basket/Basket.API/Basket.API.csproj b/src/Services/Basket/Basket.API/Basket.API.csproj index 48db069a9..f7a2943ff 100644 --- a/src/Services/Basket/Basket.API/Basket.API.csproj +++ b/src/Services/Basket/Basket.API/Basket.API.csproj @@ -13,23 +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 ccb81d25f..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.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 userName = User.FindFirst(x => x.Type == "unique_name").Value; + var userId = _identityService.GetUserIdentity(); basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ? guid : basketCheckout.RequestId; @@ -71,6 +69,8 @@ public async Task Checkout([FromBody]BasketCheckout basketCheckou return BadRequest(); } + var userName = User.FindFirst(x => x.Type == "unique_name").Value; + var eventMessage = new UserCheckoutAcceptedIntegrationEvent(userId, userName, basketCheckout.City, basketCheckout.Street, basketCheckout.State, basketCheckout.Country, basketCheckout.ZipCode, basketCheckout.CardNumber, basketCheckout.CardHolderName, basketCheckout.CardExpiration, basketCheckout.CardSecurityNumber, basketCheckout.CardTypeId, basketCheckout.Buyer, basketCheckout.RequestId, basket); @@ -78,17 +78,28 @@ public async Task Checkout([FromBody]BasketCheckout basketCheckou // 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 0f169c4a0..f3a466731 100644 --- a/src/Services/Basket/Basket.API/Dockerfile +++ b/src/Services/Basket/Basket.API/Dockerfile @@ -1,20 +1,23 @@ -ARG configuration - -FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base WORKDIR /app EXPOSE 80 -FROM microsoft/dotnet:2.2-sdk AS build -ARG configuration +FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src COPY . . WORKDIR /src/src/Services/Basket/Basket.API RUN dotnet restore -nowarn:msb3202,nu1503 -RUN dotnet build --no-restore -c ${configuration} -o /app +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 ARG configuration -RUN dotnet publish --no-restore -c ${configuration} -o /app +RUN dotnet publish --no-restore -c release -o /app FROM base AS final WORKDIR /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 @@ public void Apply(Operation operation, OperationFilterContext context) 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 @@ private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, dec 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 @@ public IEnumerable GetUsers() { 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 @@ public async Task GetBasketAsync(string customerId) 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 19bbf2c24..893a52400 100644 --- a/src/Services/Basket/Basket.API/Program.cs +++ b/src/Services/Basket/Basket.API/Program.cs @@ -4,37 +4,88 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Serilog; +using System; using System.IO; 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) => - { - config.AddEnvironmentVariables(); - }) - .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.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.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 System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; -using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Basket.API { @@ -47,26 +50,23 @@ public Startup(IConfiguration configuration) // 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 @@ public IServiceProvider ConfigureServices(IServiceCollection services) services.AddCors(options => { options.AddPolicy("CorsPolicy", - builder => builder.AllowAnyOrigin() + builder => builder + .SetIsOriginAllowed((host) => true) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); @@ -180,8 +181,8 @@ public IServiceProvider ConfigureServices(IServiceCollection services) // 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 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF 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 @@ private void RegisterAppInsights(IServiceCollection services) if (orchestratorType?.ToUpper() == "K8S") { // Enable K8s telemetry initializer - services.EnableKubernetes(); + services.AddApplicationInsightsKubernetesEnricher(); } if (orchestratorType?.ToUpper() == "SF") { @@ -309,6 +316,42 @@ private void ConfigureEventBus(IApplicationBuilder app) 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 16d56d94d..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", @@ -14,5 +17,11 @@ "ApplicationInsights": { "InstrumentationKey": "" }, - "EventBusRetryCount": 5 + "EventBusRetryCount": 5, + "UseVault": false, + "Vault": { + "Name": "eshop", + "ClientId": "your-clien-id", + "ClientSecret": "your-client-secret" + } } \ No newline at end of file diff --git a/src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs b/src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs new file mode 100644 index 000000000..6b6677595 --- /dev/null +++ b/src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Http; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Basket.FunctionalTests.Base +{ + class AutoAuthorizeMiddleware + { + public const string IDENTITY_ID = "9e3163b9-1ae6-4652-9dc6-7898ab7b7a00"; + + private readonly RequestDelegate _next; + + public AutoAuthorizeMiddleware(RequestDelegate rd) + { + _next = rd; + } + + public async Task Invoke(HttpContext httpContext) + { + var identity = new ClaimsIdentity("cookies"); + + identity.AddClaim(new Claim("sub", IDENTITY_ID)); + identity.AddClaim(new Claim("unique_name", IDENTITY_ID)); + + httpContext.User.AddIdentity(identity); + + await _next.Invoke(httpContext); + } + } +} diff --git a/src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs b/src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs new file mode 100644 index 000000000..b5a62ce9d --- /dev/null +++ b/src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using System.IO; +using System.Reflection; + +namespace Basket.FunctionalTests.Base +{ + public class BasketScenarioBase + { + private const string ApiUrlBase = "api/v1/basket"; + + public TestServer CreateServer() + { + var path = Assembly.GetAssembly(typeof(BasketScenarioBase)) + .Location; + + var hostBuilder = new WebHostBuilder() + .UseContentRoot(Path.GetDirectoryName(path)) + .ConfigureAppConfiguration(cb => + { + cb.AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables(); + }).UseStartup(); + + return new TestServer(hostBuilder); + } + + public static class Get + { + public static string GetBasket(int id) + { + return $"{ApiUrlBase}/{id}"; + } + } + + public static class Post + { + public static string Basket = $"{ApiUrlBase}/"; + public static string CheckoutOrder = $"{ApiUrlBase}/checkout"; + } + } +} diff --git a/test/Services/FunctionalTests/Services/Basket/BasketTestsStartup.cs b/src/Services/Basket/Basket.FunctionalTests/Base/BasketTestStartup.cs similarity index 61% rename from test/Services/FunctionalTests/Services/Basket/BasketTestsStartup.cs rename to src/Services/Basket/Basket.FunctionalTests/Base/BasketTestStartup.cs index a6695c12f..4d77f6dc5 100644 --- a/test/Services/FunctionalTests/Services/Basket/BasketTestsStartup.cs +++ b/src/Services/Basket/Basket.FunctionalTests/Base/BasketTestStartup.cs @@ -1,14 +1,12 @@ -using FunctionalTests.Middleware; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Builder; using Microsoft.eShopOnContainers.Services.Basket.API; using Microsoft.Extensions.Configuration; -namespace FunctionalTests.Services.Basket +namespace Basket.FunctionalTests.Base { - public class BasketTestsStartup : Startup + class BasketTestsStartup : Startup { - public BasketTestsStartup(IConfiguration configuration) : base(configuration) + public BasketTestsStartup(IConfiguration env) : base(env) { } @@ -22,6 +20,6 @@ protected override void ConfigureAuth(IApplicationBuilder app) { base.ConfigureAuth(app); } - } + } } } diff --git a/test/Services/IntegrationTests/Services/Extensions/HttpClientExtensions.cs b/src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs similarity index 79% rename from test/Services/IntegrationTests/Services/Extensions/HttpClientExtensions.cs rename to src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs index 00ed918b6..f2da0b13e 100644 --- a/test/Services/IntegrationTests/Services/Extensions/HttpClientExtensions.cs +++ b/src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs @@ -1,17 +1,17 @@ using Microsoft.AspNetCore.TestHost; using System; -using System.Collections.Generic; using System.Net.Http; -using System.Text; -namespace IntegrationTests.Services.Extensions +namespace Basket.FunctionalTests.Base { static class HttpClientExtensions { public static HttpClient CreateIdempotentClient(this TestServer server) { var client = server.CreateClient(); + client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString()); + return client; } } diff --git a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj new file mode 100644 index 000000000..e781b218d --- /dev/null +++ b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj @@ -0,0 +1,36 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + PreserveNewest + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + diff --git a/test/Services/IntegrationTests/Services/Basket/BasketScenarios.cs b/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs similarity index 93% rename from test/Services/IntegrationTests/Services/Basket/BasketScenarios.cs rename to src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs index 747efc8ed..83d507ade 100644 --- a/test/Services/IntegrationTests/Services/Basket/BasketScenarios.cs +++ b/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs @@ -1,14 +1,14 @@ -using IntegrationTests.Services.Extensions; +using Basket.FunctionalTests.Base; using Microsoft.eShopOnContainers.Services.Basket.API.Model; using Newtonsoft.Json; using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; using Xunit; -namespace IntegrationTests.Services.Basket +namespace Basket.FunctionalTests { public class BasketScenarios : BasketScenarioBase @@ -17,7 +17,7 @@ public class BasketScenarios public async Task Post_basket_and_response_ok_status_code() { using (var server = CreateServer()) - { + { var content = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json"); var response = await server.CreateClient() .PostAsync(Post.Basket, content); @@ -44,10 +44,12 @@ public async Task Send_Checkout_basket_and_response_ok_status_code() using (var server = CreateServer()) { var contentBasket = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json"); + await server.CreateClient() .PostAsync(Post.Basket, contentBasket); var contentCheckout = new StringContent(BuildCheckout(), UTF8Encoding.UTF8, "application/json"); + var response = await server.CreateIdempotentClient() .PostAsync(Post.CheckoutOrder, contentCheckout); @@ -57,7 +59,7 @@ await server.CreateClient() string BuildBasket() { - var order = new CustomerBasket("1234"); + var order = new CustomerBasket(AutoAuthorizeMiddleware.IDENTITY_ID); order.Items.Add(new BasketItem { diff --git a/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs b/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs new file mode 100644 index 000000000..84d41aa1a --- /dev/null +++ b/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs @@ -0,0 +1,90 @@ +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; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Basket.FunctionalTests +{ + public class RedisBasketRepositoryTests + : BasketScenarioBase + { + + [Fact] + public async Task UpdateBasket_return_and_add_basket() + { + using (var server = CreateServer()) + { + var redis = server.Host.Services.GetRequiredService(); + + 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() + { + + using (var server = CreateServer()) + { + var redis = server.Host.Services.GetRequiredService(); + + var redisBasketRepository = BuildBasketRepository(redis); + + var basket = await redisBasketRepository.UpdateBasketAsync(new CustomerBasket("customerId") + { + BuyerId = "buyerId", + Items = BuildBasketItems() + }); + + var deleteResult = await redisBasketRepository.DeleteBasketAsync("buyerId"); + + var result = await redisBasketRepository.GetBasketAsync(basket.BuyerId); + + Assert.True(deleteResult); + Assert.Null(result); + } + } + + RedisBasketRepository BuildBasketRepository(ConnectionMultiplexer connMux) + { + var loggerFactory = new LoggerFactory(); + return new RedisBasketRepository(loggerFactory, connMux); + } + + List BuildBasketItems() + { + return new List() + { + new BasketItem() + { + Id = "basketId", + PictureUrl = "pictureurl", + ProductId = "productId", + ProductName = "productName", + Quantity = 1, + UnitPrice = 1 + } + }; + } + } +} diff --git a/test/Services/IntegrationTests/Services/Basket/appsettings.json b/src/Services/Basket/Basket.FunctionalTests/appsettings.json similarity index 98% rename from test/Services/IntegrationTests/Services/Basket/appsettings.json rename to src/Services/Basket/Basket.FunctionalTests/appsettings.json index f83d4de4b..5e8dbc02e 100644 --- a/test/Services/IntegrationTests/Services/Basket/appsettings.json +++ b/src/Services/Basket/Basket.FunctionalTests/appsettings.json @@ -1,4 +1,4 @@ -{ +{ "Logging": { "IncludeScopes": false, "LogLevel": { @@ -13,5 +13,3 @@ "EventBusConnection": "localhost", "SubscriptionClientName": "Basket" } - - diff --git a/test/Services/UnitTest/Basket/Application/BasketWebApiTest.cs b/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs similarity index 67% rename from test/Services/UnitTest/Basket/Application/BasketWebApiTest.cs rename to src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs index 7e17a9980..0045ce4aa 100644 --- a/test/Services/UnitTest/Basket/Application/BasketWebApiTest.cs +++ b/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs @@ -1,12 +1,15 @@ using Basket.API.IntegrationEvents.Events; using Basket.API.Model; +using Microsoft.AspNetCore.Http; 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; +using System.Security.Claims; using System.Threading.Tasks; using Xunit; using IBasketIdentityService = Microsoft.eShopOnContainers.Services.Basket.API.Services.IIdentityService; @@ -18,12 +21,14 @@ public class BasketWebApiTest 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] @@ -38,13 +43,18 @@ public async Task Get_customer_basket_success() _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); _serviceBusMock.Setup(x => x.Publish(It.IsAny())); + //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); } @@ -59,16 +69,20 @@ public async Task Post_customer_basket_success() .Returns(Task.FromResult(fakeCustomerBasket)); _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); _serviceBusMock.Setup(x => x.Publish(It.IsAny())); + //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() @@ -77,11 +91,15 @@ public async Task Doing_Checkout_Without_Basket_Should_Return_Bad_Request() _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) .Returns(Task.FromResult((CustomerBasket)null)); _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + //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); } @@ -90,20 +108,37 @@ public async Task Doing_Checkout_Wit_Basket_Should_Publish_UserCheckoutAccepted_ { var fakeCustomerId = "1"; var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId); + _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) .Returns(Task.FromResult(fakeCustomerBasket)); + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); - //Act + var basketController = new BasketController( - _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); + + basketController.ControllerContext = new ControllerContext() + { + HttpContext = new DefaultHttpContext() + { + User = new ClaimsPrincipal( + new ClaimsIdentity(new Claim[] { new Claim("unique_name", "testuser") })) + } + }; + + //Act + var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as AcceptedResult; - var result = await basketController.Checkout(new BasketCheckout(), Guid.NewGuid().ToString()) as AcceptedResult; _serviceBusMock.Verify(mock => mock.Publish(It.IsAny()), Times.Once); + Assert.NotNull(result); } private CustomerBasket GetCustomerBasketFake(string fakeCustomerId) - { + { return new CustomerBasket(fakeCustomerId) { Items = new List() diff --git a/test/Services/UnitTest/Basket/Application/CartControllerTest.cs b/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs similarity index 99% rename from test/Services/UnitTest/Basket/Application/CartControllerTest.cs rename to src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs index 63f74cf35..bdbf8afd4 100644 --- a/test/Services/UnitTest/Basket/Application/CartControllerTest.cs +++ b/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs @@ -6,7 +6,6 @@ using Moq; using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Xunit; using BasketModel = Microsoft.eShopOnContainers.WebMVC.ViewModels.Basket; diff --git a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj new file mode 100644 index 000000000..bf5cb593a --- /dev/null +++ b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj @@ -0,0 +1,26 @@ + + + + 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 96a5c8b92..b90279330 100644 --- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj +++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj @@ -32,24 +32,35 @@ - - - + + + + + + + + - - - + + + + + + + + + + + + + - - - - diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 20d1b1215..d9fa4002e 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -28,7 +28,7 @@ public CatalogController(CatalogContext context, IOptionsSnapshot), (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,34 +62,36 @@ public async Task Items([FromQuery]int pageSize = 10, [FromQuery] 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 idsToSelect = numIds + .Select(id => id.Value); + + 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(typeof(CatalogItem),(int)HttpStatusCode.OK)] - public async Task GetItemById(int id) + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(CatalogItem), (int)HttpStatusCode.OK)] + public async Task> ItemByIdAsync(int id) { if (id <= 0) { @@ -92,11 +102,12 @@ public async Task GetItemById(int id) var baseUri = _settings.PicBaseUrl; var azureStorageEnabled = _settings.AzureStorageEnabled; + item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); if (item != null) { - return Ok(item); + return item; } return NotFound(); @@ -104,11 +115,10 @@ public async Task GetItemById(int id) // 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(); @@ -121,25 +131,45 @@ public async Task Items(string name, [FromQuery]int pageSize = 10 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/null[?pageSize=3&pageIndex=10] + // GET api/v1/[controller]/items/type/1/brand[?pageSize=3&pageIndex=10] [HttpGet] - [Route("[action]/type/{catalogTypeId}/brand/{catalogBrandId}")] + [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; - if (catalogTypeId.HasValue) + root = root.Where(ci => ci.CatalogTypeId == catalogTypeId); + + if (catalogBrandId.HasValue) { - root = root.Where(ci => ci.CatalogTypeId == catalogTypeId); + root = root.Where(ci => ci.CatalogBrandId == catalogBrandId); } + var totalItems = await root + .LongCountAsync(); + + var itemsOnPage = await root + .Skip(pageSize * pageIndex) + .Take(pageSize) + .ToListAsync(); + + itemsOnPage = ChangeUriPlaceholder(itemsOnPage); + + return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); + } + + // GET api/v1/[controller]/items/type/all/brand[?pageSize=3&pageIndex=10] + [HttpGet] + [Route("items/type/all/brand/{catalogBrandId:int?}")] + [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] + public async Task>> ItemsByBrandIdAsync(int? catalogBrandId, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0) + { + var root = (IQueryable)_catalogContext.CatalogItems; + if (catalogBrandId.HasValue) { root = root.Where(ci => ci.CatalogBrandId == catalogBrandId); @@ -155,34 +185,25 @@ public async Task Items(int? catalogTypeId, int? catalogBrandId, 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 @@ -190,10 +211,9 @@ public async Task CatalogBrands() [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) { @@ -203,7 +223,6 @@ public async Task UpdateProduct([FromBody]CatalogItem productToUp var oldPrice = catalogItem.Price; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; - // Update current product catalogItem = productToUpdate; _catalogContext.CatalogItems.Update(catalogItem); @@ -224,14 +243,14 @@ public async Task UpdateProduct([FromBody]CatalogItem productToUp 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 { @@ -242,18 +261,20 @@ public async Task CreateProduct([FromBody]CatalogItem product) 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 4af51bd90..4e41d4e6c 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs @@ -9,8 +9,9 @@ // 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 @@ public PicController(IHostingEnvironment env, [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 3e9b591f7..b9b8816aa 100644 --- a/src/Services/Catalog/Catalog.API/Dockerfile +++ b/src/Services/Catalog/Catalog.API/Dockerfile @@ -1,22 +1,24 @@ -ARG configuration - -FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base WORKDIR /app EXPOSE 80 -RUN echo "Build configuration: ${configuration}" -FROM microsoft/dotnet:2.2-sdk AS build -ARG configuration +FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src COPY . . WORKDIR /src/src/Services/Catalog/Catalog.API RUN dotnet restore -nowarn:msb3202,nu1503 -RUN dotnet build --no-restore -c ${configuration} -o /app +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 ARG configuration RUN echo "Build configuration: ${configuration}" -RUN dotnet publish --no-restore -c ${configuration} -o /app +RUN dotnet publish --no-restore -c release -o /app FROM base AS final WORKDIR /app diff --git a/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs b/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs index 71b9d88a3..fa2c3f60e 100644 --- a/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs +++ b/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs @@ -9,9 +9,12 @@ public static class CatalogItemExtensions { public static void FillProductUrl(this CatalogItem item, string picBaseUrl, bool azureStorageEnabled) { - item.PictureUri = azureStorageEnabled + if (item != null) + { + item.PictureUri = azureStorageEnabled ? picBaseUrl + item.PictureFileName : picBaseUrl.Replace("[0]", item.Id.ToString()); + } } } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs index 5314a0201..13fbaadaa 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs @@ -85,14 +85,14 @@ private IEnumerable GetCatalogBrandsFromFile(string contentRootPat } 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); } @@ -140,14 +140,14 @@ private IEnumerable GetCatalogTypesFromFile(string contentRootPath, } 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); } @@ -195,7 +195,7 @@ private IEnumerable GetCatalogItemsFromFile(string contentRootPath, } catch (Exception ex) { - logger.LogError(ex.Message); + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return GetPreconfiguredItems(); } @@ -206,7 +206,7 @@ private IEnumerable GetCatalogItemsFromFile(string contentRootPath, .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); } @@ -372,10 +372,13 @@ private void GetCatalogItemPictures(string contentRootPath, string picturePath, private static void CleanUpItemPictures(string picturePath) { - DirectoryInfo directory = new DirectoryInfo(picturePath); - foreach (FileInfo file in directory.GetFiles()) + if (picturePath != null) { - file.Delete(); + DirectoryInfo directory = new DirectoryInfo(picturePath); + foreach (FileInfo file in directory.GetFiles()) + { + file.Delete(); + } } } @@ -417,7 +420,7 @@ private Policy CreatePolicy( ILogger logger, string prefix,i 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 @@ public void OnException(ExceptionContext context) if (env.IsDevelopment()) { - json.DeveloperMeesage = context.Exception; + json.DeveloperMessage = context.Exception; } context.Result = new InternalServerErrorObjectResult(json); @@ -62,7 +62,7 @@ private class JsonErrorResponse { 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.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 @@ public class CatalogIntegrationEventService : ICatalogIntegrationEventService 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 @@ public CatalogIntegrationEventService(IEventBus eventBus, CatalogContext catalog 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); - confirmedOrderStockItems.Add(confirmedOrderStockItem); - } + 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); - var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock) - ? (IntegrationEvent) new OrderStockRejectedIntegrationEvent(command.OrderId, confirmedOrderStockItems) - : new OrderStockConfirmedIntegrationEvent(command.OrderId); + confirmedOrderStockItems.Add(confirmedOrderStockItem); + } - await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent); - await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent); + 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); + + } } } } \ 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); - catalogItem.RemoveStock(orderStockItem.Units); - } + //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(); + 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 4a5e71109..051b65600 100644 --- a/src/Services/Catalog/Catalog.API/Program.cs +++ b/src/Services/Catalog/Catalog.API/Program.cs @@ -8,49 +8,100 @@ 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((_,__)=> { }) - .SeedDbContext((services) => { - services.GetService().SeedAsync().Wait(); - }) - .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) => - { - config.AddEnvironmentVariables(); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - }) - .Build(); + .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(); + } } } \ 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 72a9100f2..891a0d914 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -1,9 +1,9 @@ using Autofac; using Autofac.Extensions.DependencyInjection; -using Catalog.API.Extensions; using global::Catalog.API.Infrastructure.Filters; using global::Catalog.API.IntegrationEvents; using Microsoft.ApplicationInsights.Extensibility; +using Catalog.API.Extensions; using Microsoft.ApplicationInsights.ServiceFabric; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -23,13 +23,15 @@ 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 { @@ -46,6 +48,7 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services) { services.AddAppInsight(Configuration) .AddCustomMVC(Configuration) + .AddCustomHealthCheck(Configuration) .AddCustomDbContext(Configuration) .AddCustomOptions(Configuration) .AddIntegrationServices(Configuration) @@ -58,24 +61,32 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services) } + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //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"); @@ -108,7 +119,7 @@ public static IServiceCollection AddAppInsight(this IServiceCollection services, if (orchestratorType?.ToUpper() == "K8S") { // Enable K8s telemetry initializer - services.EnableKubernetes(); + services.AddApplicationInsightsKubernetesEnricher(); } if (orchestratorType?.ToUpper() == "SF") { @@ -121,33 +132,19 @@ public static IServiceCollection AddAppInsight(this IServiceCollection services, } 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()); @@ -156,6 +153,50 @@ public static IServiceCollection AddCustomMVC(this IServiceCollection services, 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 => @@ -224,10 +265,7 @@ public static IServiceCollection AddSwagger(this IServiceCollection services) Description = "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample", TermsOfService = "Terms Of Service" }); - - options.OperationFilter(); }); - return services; } diff --git a/src/Services/Catalog/Catalog.API/appsettings.json b/src/Services/Catalog/Catalog.API/appsettings.json index 497847f92..b9e06f898 100644 --- a/src/Services/Catalog/Catalog.API/appsettings.json +++ b/src/Services/Catalog/Catalog.API/appsettings.json @@ -5,12 +5,15 @@ "PicBaseUrl": "http://localhost:5101/api/v1/catalog/items/[0]/pic/", "UseCustomizationData": true, "UseCustomizationDataAI": true, - "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, @@ -20,5 +23,11 @@ "InstrumentationKey": "" }, "EventBusConnection": "localhost", - "EventBusRetryCount": 5 + "EventBusRetryCount": 5, + "UseVault": false, + "Vault": { + "Name": "eshop", + "ClientId": "your-clien-id", + "ClientSecret": "your-client-secret" + }, } diff --git a/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj b/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj new file mode 100644 index 000000000..07e9e2991 --- /dev/null +++ b/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj @@ -0,0 +1,50 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + diff --git a/test/Services/IntegrationTests/Services/Catalog/CatalogScenarioBase.cs b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs similarity index 67% rename from test/Services/IntegrationTests/Services/Catalog/CatalogScenarioBase.cs rename to src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs index 57d8c6ef1..46bdb07d1 100644 --- a/test/Services/IntegrationTests/Services/Catalog/CatalogScenarioBase.cs +++ b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs @@ -1,25 +1,33 @@ -namespace IntegrationTests.Services.Catalog -{ - using Microsoft.AspNetCore; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.TestHost; - using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; - using Microsoft.eShopOnContainers.Services.Catalog.API; - using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Options; - using System.IO; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; +using Microsoft.eShopOnContainers.Services.Catalog.API; +using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System.IO; +using System.Reflection; - public class CatalogScenarioBase +namespace Catalog.FunctionalTests +{ + public class CatalogScenariosBase { public TestServer CreateServer() { - var webHostBuilder = WebHost.CreateDefaultBuilder(); - webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory() + "\\Services\\Catalog"); - webHostBuilder.UseStartup(); + var path = Assembly.GetAssembly(typeof(CatalogScenariosBase)) + .Location; - var testServer = new TestServer(webHostBuilder); + var hostBuilder = new WebHostBuilder() + .UseContentRoot(Path.GetDirectoryName(path)) + .ConfigureAppConfiguration(cb => + { + cb.AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables(); + }).UseStartup(); + + var testServer = new TestServer(hostBuilder); testServer.Host .MigrateDbContext((context, services) => @@ -44,7 +52,7 @@ public static class Get public static string Items(bool paginated = false) { - return paginated + return paginated ? "api/v1/catalog/items" + Paginated(PageIndex, PageCount) : "api/v1/catalog/items"; } diff --git a/test/Services/IntegrationTests/Services/Catalog/CatalogScenarios.cs b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarios.cs similarity index 94% rename from test/Services/IntegrationTests/Services/Catalog/CatalogScenarios.cs rename to src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarios.cs index 6414a57ea..abb6a6cbf 100644 --- a/test/Services/IntegrationTests/Services/Catalog/CatalogScenarios.cs +++ b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarios.cs @@ -1,13 +1,11 @@ -using System; -using System.Net; +using System.Net; +using System.Threading.Tasks; +using Xunit; -namespace IntegrationTests.Services.Catalog +namespace Catalog.FunctionalTests { - using System.Threading.Tasks; - using Xunit; - public class CatalogScenarios - : CatalogScenarioBase + : CatalogScenariosBase { [Fact] public async Task Get_get_all_catalogitems_and_response_ok_status_code() @@ -53,7 +51,7 @@ public async Task Get_get_catalogitem_by_id_and_response_not_found_status_code() var response = await server.CreateClient() .GetAsync(Get.ItemById(int.MaxValue)); - Assert.Equal( HttpStatusCode.NotFound, response.StatusCode); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } } diff --git a/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogBrands.csv b/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogBrands.csv new file mode 100644 index 000000000..95c9ee64c --- /dev/null +++ b/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogBrands.csv @@ -0,0 +1,8 @@ +CatalogBrand +Azure +.NET +Visual Studio +SQL Server +Other +CatalogBrandTestOne +CatalogBrandTestTwo \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogItems.csv b/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogItems.csv new file mode 100644 index 000000000..0257216d4 --- /dev/null +++ b/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogItems.csv @@ -0,0 +1,14 @@ +CatalogTypeName,CatalogBrandName,Description,Name,Price,PictureFileName,availablestock,onreorder +T-Shirt,.NET,".NET Bot Black Hoodie, and more",.NET Bot Black Hoodie,19.5,1.png,100,false +Mug,.NET,.NET Black & White Mug,.NET Black & White Mug,8.50,2.png,89,true +T-Shirt,Other,Prism White T-Shirt,Prism White T-Shirt,12,3.png,56,false +T-Shirt,.NET,.NET Foundation T-shirt,.NET Foundation T-shirt,12,4.png,120,false +Sheet,Other,Roslyn Red Sheet,Roslyn Red Sheet,8.5,5.png,55,false +T-Shirt,.NET,.NET Blue Hoodie,.NET Blue Hoodie,12,6.png,17,false +T-Shirt,Other,Roslyn Red T-Shirt,Roslyn Red T-Shirt",12,7.png,8,false +T-Shirt,Other,Kudu Purple Hoodie,Kudu Purple Hoodie,8.5,8.png,34,false +Mug,Other,Cup White Mug,Cup White Mug,12,9.png,76,false +Sheet,.NET,.NET Foundation Sheet,.NET Foundation Sheet,12,10.png,11,false +Sheet,.NET,Cup Sheet,Cup Sheet,8.5,11.png,3,false +T-Shirt,Other,Prism White TShirt,Prism White TShirt,12,12.png,0,false +Mug, Other, De los Palotes, pepito, 12, 12.png, 0, false \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogItems.zip b/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogItems.zip new file mode 100644 index 000000000..6d3edfb3c Binary files /dev/null and b/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogItems.zip differ diff --git a/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogTypes.csv b/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogTypes.csv new file mode 100644 index 000000000..ae636c265 --- /dev/null +++ b/src/Services/Catalog/Catalog.FunctionalTests/Setup/CatalogTypes.csv @@ -0,0 +1,7 @@ +CatalogType +Mug +T-Shirt +Sheet +USB Memory Stick +CatalogTypeTestOne +CatalogTypeTestTwo \ No newline at end of file diff --git a/test/Services/FunctionalTests/Services/Catalog/appsettings.json b/src/Services/Catalog/Catalog.FunctionalTests/appsettings.json similarity index 100% rename from test/Services/FunctionalTests/Services/Catalog/appsettings.json rename to src/Services/Catalog/Catalog.FunctionalTests/appsettings.json diff --git a/test/Services/UnitTest/Catalog/Application/CatalogControllerTest.cs b/src/Services/Catalog/Catalog.UnitTests/Application/CatalogControllerTest.cs similarity index 100% rename from test/Services/UnitTest/Catalog/Application/CatalogControllerTest.cs rename to src/Services/Catalog/Catalog.UnitTests/Application/CatalogControllerTest.cs diff --git a/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj b/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj new file mode 100644 index 000000000..6a9a2bf99 --- /dev/null +++ b/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj @@ -0,0 +1,25 @@ + + + + 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 309c78fbf..4187f7d0c 100644 --- a/src/Services/Identity/Identity.API/Configuration/Config.cs +++ b/src/Services/Identity/Identity.API/Configuration/Config.cs @@ -17,7 +17,8 @@ public static IEnumerable GetApis() 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 @@ public static IEnumerable GetResources() } // 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 @@ public static IEnumerable GetClients(Dictionary clientsUr "locations", "marketing", "webshoppingagg", - "orders.signalrhub" - } + "orders.signalrhub", + "webhooks" + }, }, new Client { @@ -74,7 +76,7 @@ public static IEnumerable GetClients(Dictionary clientsUr 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 @@ public static IEnumerable GetClients(Dictionary clientsUr "basket", "locations", "marketing", - "mobileshoppingagg" + "mobileshoppingagg", + "webhooks" }, //Allow requesting refresh tokens for long lived API access AllowOfflineAccess = true, @@ -122,43 +125,43 @@ public static IEnumerable GetClients(Dictionary clientsUr "locations", "marketing", "webshoppingagg", - "orders.signalrhub" + "orders.signalrhub", + "webhooks" }, + AccessTokenLifetime = 60*60*2, // 2 hours + IdentityTokenLifetime= 60*60*2 // 2 hours }, new Client { - ClientId = "Bot", - ClientName = "Bot Client", + ClientId = "webhooksclient", + ClientName = "Webhooks Client", ClientSecrets = new List { new Secret("secret".Sha256()) }, - ClientUri = $"{clientsUrl["Bot"]}", // public uri of the client - AllowedGrantTypes = new [] { GrantType.AuthorizationCode }, // for using Azure Authentication - AllowAccessTokensViaBrowser = true, + ClientUri = $"{clientsUrl["WebhooksWeb"]}", // public uri of the client + AllowedGrantTypes = GrantTypes.Hybrid, + AllowAccessTokensViaBrowser = false, RequireConsent = false, AllowOfflineAccess = true, AlwaysIncludeUserClaimsInIdToken = true, RedirectUris = new List { - $"{clientsUrl["Bot"]}/account/signin-oidc", - "https://token.botframework.com/.auth/web/redirect" + $"{clientsUrl["WebhooksWeb"]}/signin-oidc" }, PostLogoutRedirectUris = new List { - $"{clientsUrl["Bot"]}/account/signout-callback-oidc" + $"{clientsUrl["WebhooksWeb"]}/signout-callback-oidc" }, AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.OfflineAccess, - "orders", - "basket", - "locations", - "marketing", - "webshoppingagg" + "webhooks" }, + AccessTokenLifetime = 60*60*2, // 2 hours + IdentityTokenLifetime= 60*60*2 // 2 hours }, new Client { @@ -190,7 +193,8 @@ public static IEnumerable GetClients(Dictionary clientsUr "basket", "locations", "marketing", - "webshoppingagg" + "webshoppingagg", + "webhooks" }, }, new Client @@ -282,8 +286,22 @@ public static IEnumerable GetClients(Dictionary clientsUr { "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.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 @@ public async Task Login(string returnUrl) 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 @@ public async Task Login(LoginViewModel model) 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 @@ public async Task Login(LoginViewModel model) }; }; - 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 @@ public async Task Login(LoginViewModel model) 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 @@ async Task BuildLoginViewModelAsync(string returnUrl, Authorizat }; } - 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 @@ public async Task Logout(LogoutViewModel model) try { - + // hack: try/catch to handle social providers that throw await HttpContext.SignOutAsync(idp, new AuthenticationProperties { @@ -202,13 +208,15 @@ public async Task Logout(LogoutViewModel model) } 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 @@ public async Task DeviceLogOut(string redirectUrl) 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 91daabbfb..40f8d62fd 100644 --- a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs @@ -58,7 +58,7 @@ public async Task SeedAsync(ApplicationDbContext context,IHostingEnvironment env { 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); } @@ -93,7 +93,7 @@ private IEnumerable GetUsersFromFile(string contentRootPath, st } catch (Exception ex) { - logger.LogError(ex.Message); + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return GetDefaultUser(); } @@ -102,7 +102,7 @@ private IEnumerable GetUsersFromFile(string contentRootPath, st .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(); @@ -214,7 +214,7 @@ static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogg 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; } @@ -236,14 +236,14 @@ static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogg } 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 25de14606..653c6fded 100644 --- a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs @@ -17,19 +17,19 @@ public async Task SeedAsync(ConfigurationDbContext context,IConfiguration config { //callbacks urls from config: - var clientUrls = new Dictionary - { - {"Mvc", configuration.GetValue("MvcClient")}, - {"Bot", configuration.GetValue("BotClient")}, - {"Spa", configuration.GetValue("SpaClient")}, - {"Xamarin", configuration.GetValue("XamarinCallback")}, - {"LocationsApi", configuration.GetValue("LocationApiClient")}, - {"MarketingApi", configuration.GetValue("MarketingApiClient")}, - {"BasketApi", configuration.GetValue("BasketApiClient")}, - {"OrderingApi", configuration.GetValue("OrderingApiClient")}, - {"MobileShoppingAgg", configuration.GetValue("MobileShoppingAggClient")}, - {"WebShoppingAgg", configuration.GetValue("WebShoppingAggClient")} - }; + var clientUrls = new Dictionary(); + + clientUrls.Add("Mvc", configuration.GetValue("MvcClient")); + clientUrls.Add("Spa", configuration.GetValue("SpaClient")); + clientUrls.Add("Xamarin", configuration.GetValue("XamarinCallback")); + clientUrls.Add("LocationsApi", configuration.GetValue("LocationApiClient")); + clientUrls.Add("MarketingApi", configuration.GetValue("MarketingApiClient")); + clientUrls.Add("BasketApi", configuration.GetValue("BasketApiClient")); + 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 013d31765..df54a096c 100644 --- a/src/Services/Identity/Identity.API/Dockerfile +++ b/src/Services/Identity/Identity.API/Dockerfile @@ -1,28 +1,13 @@ -ARG configuration -ARG NODE_IMAGE=node:8.11 - -FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base WORKDIR /app EXPOSE 80 -FROM microsoft/dotnet:2.2-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 -ARG configuration -WORKDIR /src/src/Services/Identity/Identity.API/wwwroot -COPY --from=node-build /web/wwwroot . +FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src COPY . . WORKDIR /src/src/Services/Identity/Identity.API RUN dotnet restore -nowarn:msb3202,nu1503 -RUN dotnet build --no-restore -c ${configuration} -o /app +RUN dotnet build --no-restore -c Release -o /app FROM build AS publish ARG configuration diff --git a/src/Services/Identity/Identity.API/Identity.API.csproj b/src/Services/Identity/Identity.API/Identity.API.csproj index 44cd98cb6..e8a2c7bd8 100644 --- a/src/Services/Identity/Identity.API/Identity.API.csproj +++ b/src/Services/Identity/Identity.API/Identity.API.csproj @@ -13,17 +13,28 @@ - - - + + + + + + + - - - - - + + + + + + + + + + + + @@ -39,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 7b73838e6..8204a1ebf 100644 --- a/src/Services/Identity/Identity.API/Program.cs +++ b/src/Services/Identity/Identity.API/Program.cs @@ -6,55 +6,107 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Serilog; +using System; using System.IO; 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>(); - - new ApplicationDbContextSeed() - .SeedAsync(context, env, logger, settings) - .Wait(); - }) - .MigrateDbContext((context,services)=> - { - var configuration = services.GetService(); - - new ConfigurationDbContextSeed() - .SeedAsync(context, configuration) - .Wait(); - }).Run(); + 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((_, __) => { }) + .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) => - { - config.AddEnvironmentVariables(); - }) - .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 @@ public async Task ValidateCredentials(ApplicationUser user, string passwor 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 f44f57bd3..87ae948d6 100644 --- a/src/Services/Identity/Identity.API/Startup.cs +++ b/src/Services/Identity/Identity.API/Startup.cs @@ -7,6 +7,7 @@ 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; @@ -15,11 +16,13 @@ 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 { @@ -40,12 +43,12 @@ public IServiceProvider ConfigureServices(IServiceCollection services) // 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() @@ -53,7 +56,7 @@ public IServiceProvider ConfigureServices(IServiceCollection services) services.Configure(Configuration); - services.AddMvc() + services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2) .AddControllersAsServices(); services.AddAIServices(); @@ -67,16 +70,12 @@ public IServiceProvider ConfigureServices(IServiceCollection services) .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(); @@ -84,30 +83,34 @@ public IServiceProvider ConfigureServices(IServiceCollection services) 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); @@ -118,10 +121,10 @@ public IServiceProvider ConfigureServices(IServiceCollection services) // 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()) { @@ -136,14 +139,20 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF 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,9 +164,11 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF await next(); }); + app.UseForwardedHeaders(); // Adds IdentityServer app.UseIdentityServer(); + app.UseHttpsRedirection(); app.UseMvc(routes => { routes.MapRoute( @@ -176,7 +187,7 @@ private void RegisterAppInsights(IServiceCollection services) 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 @@ -