├── .dockleignore ├── .editorconfig ├── .github ├── templates │ ├── build-and-push │ │ └── action.yml │ └── deploy │ │ └── action.yml └── workflows │ ├── acrtokenpassword.yml │ ├── azure.ci.yml │ ├── azure.destroy.yml │ ├── cn.authn.ci.yml │ ├── cn.fakesmtp.ci.yml │ ├── cn.gateway.ci.yml │ ├── cn.notification.ci.yml │ ├── cn.order-monitor-client.ci.yml │ ├── cn.order-monitor-service.ci.yml │ ├── cn.orders.ci.yml │ ├── cn.pricedropnotifier.ci.yml │ ├── cn.pricewatcher.ci.yml │ ├── cn.products.ci.yml │ ├── cn.shipping.ci.yml │ ├── dapr-cd.yml │ ├── demo.rent-to-run.yml │ ├── demo.run-to-rent.yml │ ├── k8s.azure-secrets.yml │ ├── k8s.cluster-wide.cd.yml │ ├── test-sonar.yml │ └── twiliotoken.yml ├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── Makefile ├── README.md ├── assets ├── architecture-overview.jpg └── basta-spring-2023 │ ├── BASTA! Spring 2023 - Workshop - Cloud-Native - 1.5 - print.pdf │ └── README.md ├── charts ├── authentication │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml ├── fake-smtp │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── ingress.yaml │ │ ├── service-smtp.yaml │ │ └── service-web.yaml │ └── values.yaml ├── gateway │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml ├── notification │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml ├── order-monitor-client │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml ├── order-monitor-service │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ └── service.yaml │ └── values.yaml ├── orders │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml ├── price-drop-notifier │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml ├── price-watcher │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml ├── products │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml └── shipping │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ └── tests │ │ └── test-connection.yaml │ └── values.yaml ├── docker-compose-configs ├── alertmanager │ └── alertmanager.yml ├── dapr │ ├── components │ │ ├── binding-smtp-price-dropped.yaml │ │ ├── pubsub-rabbitmq-orders.yaml │ │ ├── pubsub-rabbitmq-pricedrops.yaml │ │ ├── subscription-new-orders.yaml │ │ ├── subscription-price-dropped.yaml │ │ └── subscription-processed-orders.yaml │ └── config.yml ├── grafana │ ├── dashboards │ │ ├── cn-dashboard-provider.yml │ │ └── shipping-dashboard.json │ └── datasources │ │ └── all.yml └── prometheus │ ├── cn-alerting-rules.yml │ └── prometheus.yml ├── docker-compose.yml ├── docs └── basta-2022-workshop-notes.md ├── iac ├── .gitignore ├── azure-secrets │ ├── .gitignore │ ├── .terraform.lock.hcl │ ├── _.tf │ ├── databases.tf │ ├── servicebus.tf │ └── variables.tf ├── azure │ ├── .gitignore │ ├── .terraform-version │ ├── .terraform.lock.hcl │ ├── _.tf │ ├── acr.tf │ ├── aks.tf │ ├── azure-ad.tf │ ├── datasources.tf │ ├── locals.tf │ ├── main.tf │ ├── monitor.tf │ ├── redis.tf │ ├── servicebus.tf │ ├── sql.tf │ └── variables.tf ├── dapr │ ├── .gitignore │ ├── .terraform.lock.hcl │ ├── _.tf │ ├── dapr.tf │ ├── datasources.tf │ ├── manifests │ │ ├── email.yml │ │ ├── orders_azservicebus.yml │ │ ├── orders_rabbitmq.yml │ │ ├── pricedrops_azservicebus.yml │ │ └── pricedrops_rabbitmq.yml │ └── variables.tf ├── format-all.sh ├── kubernetes │ ├── .gitignore │ ├── .terraform-version │ ├── .terraform.lock.hcl │ ├── _.tf │ ├── app.tf │ ├── cert-manager.tf │ ├── dns.tf │ ├── grafana.tf │ ├── ingress.tf │ ├── loki.tf │ ├── prometheus.tf │ ├── promtail.tf │ ├── rabbitmq.tf │ ├── values │ │ ├── grafana.yml │ │ ├── ingress.yml │ │ ├── loki.yml │ │ ├── prometheus.yml │ │ └── promtail.yml │ ├── variables.tf │ └── zipkin.tf ├── state │ ├── .gitignore │ ├── .terraform-version │ ├── .terraform.lock.hcl │ ├── locals.tf │ ├── main.tf │ ├── storage.tf │ └── variables.tf └── validate-all.sh ├── postman-collection └── cloud-native-sample.json ├── sonar-project.properties ├── src ├── AdminCli │ ├── AdminCli.csproj │ ├── CliCommands │ │ ├── CliCommandsModule.cs │ │ ├── CommandConfigurationContext.cs │ │ ├── Environment │ │ │ ├── EnvironmentCommand.cs │ │ │ ├── EnvironmentCommandsModule.cs │ │ │ ├── SetEnvironmentCommand.cs │ │ │ └── ShowEnvironmentCommand.cs │ │ ├── ICliCommand.cs │ │ └── Products │ │ │ ├── DropPriceCommand.cs │ │ │ ├── ListProductsCommand.cs │ │ │ ├── ProductCommandsModule.cs │ │ │ └── ProductsCommand.cs │ ├── Configuration │ │ ├── AppSettings.cs │ │ ├── ConfigurationModule.cs │ │ ├── IConfigurationManager.cs │ │ ├── IdentityServerSettings.cs │ │ ├── SecureFileConfigurationManager.cs │ │ └── TokenInfo.cs │ ├── HttpAccess │ │ ├── HttpAccessModule.cs │ │ ├── HttpIdentityService.cs │ │ ├── HttpService.cs │ │ └── IHttpService.cs │ ├── Infrastructure │ │ ├── CommandLineApp.cs │ │ ├── DependencyInjection.cs │ │ ├── Json.cs │ │ └── Logging.cs │ └── Program.cs ├── AuthenticationService │ ├── .dockerignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── ApiScopesProvider.cs │ ├── AuthenticationService.csproj │ ├── ClientsProvider.cs │ ├── Configuration │ │ └── IdentityServerConfig.cs │ ├── CustomProcessor.cs │ ├── Dockerfile │ ├── Extensions │ │ ├── HostingExtensions.cs │ │ └── WebApplicationBuilderExtensions.cs │ ├── IdentityResourcesProvider.cs │ ├── Pages │ │ ├── Account │ │ │ ├── AccessDenied.cshtml │ │ │ ├── AccessDenied.cshtml.cs │ │ │ ├── Login │ │ │ │ ├── Index.cshtml │ │ │ │ ├── Index.cshtml.cs │ │ │ │ ├── InputModel.cs │ │ │ │ ├── LoginOptions.cs │ │ │ │ └── ViewModel.cs │ │ │ └── Logout │ │ │ │ ├── Index.cshtml │ │ │ │ ├── Index.cshtml.cs │ │ │ │ ├── LoggedOut.cshtml │ │ │ │ ├── LoggedOut.cshtml.cs │ │ │ │ ├── LoggedOutViewModel.cs │ │ │ │ └── LogoutOptions.cs │ │ ├── Ciba │ │ │ ├── All.cshtml │ │ │ ├── All.cshtml.cs │ │ │ ├── Consent.cshtml │ │ │ ├── Consent.cshtml.cs │ │ │ ├── ConsentOptions.cs │ │ │ ├── Index.cshtml │ │ │ ├── Index.cshtml.cs │ │ │ ├── InputModel.cs │ │ │ ├── ViewModel.cs │ │ │ └── _ScopeListItem.cshtml │ │ ├── Consent │ │ │ ├── ConsentOptions.cs │ │ │ ├── Index.cshtml │ │ │ ├── Index.cshtml.cs │ │ │ ├── InputModel.cs │ │ │ ├── ViewModel.cs │ │ │ └── _ScopeListItem.cshtml │ │ ├── Device │ │ │ ├── DeviceOptions.cs │ │ │ ├── Index.cshtml │ │ │ ├── Index.cshtml.cs │ │ │ ├── InputModel.cs │ │ │ ├── Success.cshtml │ │ │ ├── Success.cshtml.cs │ │ │ ├── ViewModel.cs │ │ │ └── _ScopeListItem.cshtml │ │ ├── Diagnostics │ │ │ ├── Index.cshtml │ │ │ ├── Index.cshtml.cs │ │ │ └── ViewModel.cs │ │ ├── Extensions.cs │ │ ├── ExternalLogin │ │ │ ├── Callback.cshtml │ │ │ ├── Callback.cshtml.cs │ │ │ ├── Challenge.cshtml │ │ │ └── Challenge.cshtml.cs │ │ ├── Grants │ │ │ ├── Index.cshtml │ │ │ ├── Index.cshtml.cs │ │ │ └── ViewModel.cs │ │ ├── Home │ │ │ └── Error │ │ │ │ ├── Index.cshtml │ │ │ │ ├── Index.cshtml.cs │ │ │ │ └── ViewModel.cs │ │ ├── Index.cshtml │ │ ├── Index.cshtml.cs │ │ ├── Redirect │ │ │ ├── Index.cshtml │ │ │ └── Index.cshtml.cs │ │ ├── SecurityHeadersAttribute.cs │ │ ├── ServerSideSessions │ │ │ ├── Index.cshtml │ │ │ └── Index.cshtml.cs │ │ ├── Shared │ │ │ ├── _Layout.cshtml │ │ │ ├── _Nav.cshtml │ │ │ └── _ValidationSummary.cshtml │ │ ├── TestUsers.cs │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── TestUserProvider.cs │ ├── appsettings.json │ ├── keys │ │ └── is-signing-key-D94BBBAF652747E1565C593DA79E333B.json │ └── wwwroot │ │ ├── css │ │ ├── site.css │ │ ├── site.min.css │ │ └── site.scss │ │ ├── duende-logo.svg │ │ ├── favicon.ico │ │ ├── js │ │ ├── signin-redirect.js │ │ └── signout-redirect.js │ │ └── lib │ │ ├── bootstrap │ │ ├── LICENSE │ │ ├── README.md │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ │ ├── bootstrap4-glyphicons │ │ ├── LICENSE │ │ ├── css │ │ │ ├── bootstrap-glyphicons.css │ │ │ └── bootstrap-glyphicons.min.css │ │ ├── fonts │ │ │ └── glyphicons │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── maps │ │ │ ├── glyphicons-fontawesome.css │ │ │ ├── glyphicons-fontawesome.less │ │ │ └── glyphicons-fontawesome.min.css │ │ └── jquery │ │ ├── LICENSE.txt │ │ ├── README.md │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ ├── jquery.min.map │ │ ├── jquery.slim.js │ │ ├── jquery.slim.min.js │ │ └── jquery.slim.min.map ├── CloudNativeSample.sln ├── CloudNativeSample.sln.DotSettings ├── Gateway │ ├── .dockerignore │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── Configuration │ │ └── GatewayConfiguration.cs │ ├── Constants.cs │ ├── CustomMetrics.cs │ ├── CustomProcessor.cs │ ├── Dockerfile │ ├── Extensions │ │ └── WebApplicationBuilderExtensions.cs │ ├── Features │ │ ├── Cors.cs │ │ ├── HeaderPropagation.cs │ │ ├── RateLimiting.cs │ │ └── ResponseCompression.cs │ ├── Gateway.csproj │ ├── Models │ │ └── OrderMonitorListModel.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── TransformProviders │ │ └── DaprTransformProvider.cs │ ├── appsettings.Development.json │ ├── appsettings.DockerCompose.json │ └── appsettings.Production.json ├── NotificationService │ ├── .dockerignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── Configuration │ │ ├── Authorization.cs │ │ ├── IdentityServerConfiguration.cs │ │ └── NotificationServiceConfiguration.cs │ ├── Constants.cs │ ├── Controllers │ │ └── OrdersController.cs │ ├── CustomMetrics.cs │ ├── CustomProcessor.cs │ ├── Dockerfile │ ├── Extensions │ │ ├── HttpRequestExtensions.cs │ │ └── WebApplicationBuilderExtensions.cs │ ├── Models │ │ └── DispatchedOrder.cs │ ├── NotificationHub.cs │ ├── NotificationService.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ └── appsettings.json ├── OrderMonitorClient │ ├── .dockerignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── App.razor │ ├── Authentication │ │ ├── Authentication.razor │ │ ├── LoginDisplay.razor │ │ └── RedirectToLogin.razor │ ├── Dockerfile │ ├── Models │ │ ├── OrderModels.cs │ │ └── ProductModels.cs │ ├── OrderMonitorClient.csproj │ ├── Pages │ │ ├── Index.razor │ │ ├── OrderMonitor.razor │ │ └── ProductList.razor │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Services │ │ ├── OrderMonitorService.cs │ │ └── ProductsService.cs │ ├── Shared │ │ ├── MainLayout.razor │ │ └── NavMenu.razor │ ├── _Imports.razor │ ├── nginx.conf │ └── wwwroot │ │ ├── appsettings.docker-compose.json │ │ ├── appsettings.json │ │ ├── appsettings.k8s.json │ │ ├── favicon.ico │ │ ├── index.html │ │ └── sample-data │ │ └── weather.json ├── OrderMonitorService │ ├── .devcontainer │ │ └── devcontainer.json │ ├── .dockerignore │ ├── Dockerfile │ ├── cmd │ │ ├── api.go │ │ ├── logs.go │ │ ├── metrics.go │ │ └── tracing.go │ ├── config.json │ ├── go.mod │ ├── go.sum │ └── pkg │ │ ├── controllers │ │ ├── healthz.go │ │ └── ordermonitorservice.go │ │ ├── monitor │ │ ├── backends.go │ │ ├── config.go │ │ ├── models.go │ │ └── monitor.go │ │ └── utils │ │ └── utils.go ├── OrdersService │ ├── .dockerignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── Configuration │ │ ├── Authorization.cs │ │ ├── IdentityServerConfiguration.cs │ │ └── OrdersServiceConfiguration.cs │ ├── Controllers │ │ └── OrdersController.cs │ ├── CustomMetrics.cs │ ├── CustomProcessor.cs │ ├── Data │ │ ├── Entities │ │ │ └── Order.cs │ │ ├── OrdersServiceContext.cs │ │ └── Repositories │ │ │ ├── IOrdersRepository.cs │ │ │ └── OrdersRepository.cs │ ├── Dockerfile │ ├── Extensions │ │ ├── HttpContextExtensions.cs │ │ ├── OrderExtensions.cs │ │ └── WebApplicationBuilderExtensions.cs │ ├── Migrations │ │ ├── 20230320133222_InitialSetup.Designer.cs │ │ ├── 20230320133222_InitialSetup.cs │ │ └── OrdersServiceContextModelSnapshot.cs │ ├── Models │ │ └── OrderModels.cs │ ├── OrdersService.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ └── appsettings.json ├── PriceDropNotifierService │ ├── .dockerignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── Configuration │ │ ├── Authorization.cs │ │ ├── IdentityServerConfiguration.cs │ │ └── PriceDropNotifierServiceConfiguration.cs │ ├── Controllers │ │ └── PriceDropNotifierController.cs │ ├── CustomProcessor.cs │ ├── Dockerfile │ ├── Extensions │ │ ├── HttpRequestExtensions.cs │ │ └── WebApplicationBuilderExtensions.cs │ ├── Models │ │ └── NotificationRequest.cs │ ├── PriceDropNotifier.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ └── appsettings.json ├── PriceWatcherService │ ├── .dockerignore │ ├── Configuration │ │ ├── Authorization.cs │ │ ├── IdentityServerConfiguration.cs │ │ └── PriceWatcherServiceConfiguration.cs │ ├── Controllers │ │ ├── PriceDropController.cs │ │ └── PriceWatcherController.cs │ ├── CustomMetrics.cs │ ├── CustomProcessor.cs │ ├── Dockerfile │ ├── Entities │ │ ├── Products.cs │ │ └── Watcher.cs │ ├── Extensions │ │ └── WebApplicationBuilderExtensions.cs │ ├── Models │ │ ├── PriceDropModel.cs │ │ ├── PriceDropNotificationModel.cs │ │ ├── RegisterModel.cs │ │ └── ResponseMessage.cs │ ├── PriceWatcher.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Repositories │ │ ├── IPriceWatcherRepository.cs │ │ └── PriceWatcherRepository.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── ProductsService │ ├── .dockerignore │ ├── Configuration │ │ ├── Authorization.cs │ │ ├── IdentityServerConfiguration.cs │ │ └── ProductsServiceConfiguration.cs │ ├── Controllers │ │ └── ProductsController.cs │ ├── CustomProcessor.cs │ ├── Data │ │ ├── Entities │ │ │ └── Product.cs │ │ └── Repositories │ │ │ ├── IProductsRepository.cs │ │ │ ├── InMemoryProductsRepository.cs │ │ │ └── ProductsRepository.cs │ ├── Dockerfile │ ├── Extensions │ │ ├── ProductExtensions.cs │ │ └── WebApplicationBuilderExtensions.cs │ ├── Migrations │ │ ├── IMigration.cs │ │ ├── InitialMigration.cs │ │ └── Migrations.cs │ ├── Models │ │ └── ProductModels.cs │ ├── ProductsService.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ └── appsettings.json ├── ShippingService │ ├── .dockerignore │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Dockerfile │ ├── cmd │ │ ├── api.go │ │ ├── logs.go │ │ ├── metrics.go │ │ └── tracing.go │ ├── config.json │ ├── go.mod │ ├── go.sum │ └── pkg │ │ ├── cloudevents │ │ └── schema.go │ │ ├── dapr │ │ ├── dapr.go │ │ └── subscribe.go │ │ └── shipping │ │ ├── config.go │ │ └── shipping.go └── cn-sample │ ├── Makefile │ ├── go.mod │ ├── go.sum │ └── main.go └── tools ├── darwin_amd64 └── cn-sample ├── darwin_arm64 └── cn-sample ├── linux_amd64 └── cn-sample ├── win_amd64 └── cn-sample.exe └── win_arm64 └── cn-sample.exe /.dockleignore: -------------------------------------------------------------------------------- 1 | CIS-DI-0009 2 | -------------------------------------------------------------------------------- /.github/workflows/test-sonar.yml: -------------------------------------------------------------------------------- 1 | name: Test SonarQube Integration 2 | on: 3 | workflow_dispatch: 4 | jobs: 5 | infra: 6 | name: "SonarQube integration test" 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout Repository 10 | uses: actions/checkout@v3 11 | - uses: sonarsource/sonarqube-scan-action@master 12 | env: 13 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 14 | SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} 15 | # If you wish to fail your job when the Quality Gate is red, uncomment the 16 | # following lines. This would typically be used to fail a deployment. 17 | # - uses: sonarsource/sonarqube-quality-gate-action@master 18 | # timeout-minutes: 5 19 | # env: 20 | # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Thinktecture Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: init start stop quickstart logs cleanup 2 | 3 | init: 4 | docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions 5 | 6 | start: 7 | docker-compose up --build -d 8 | 9 | quickstart: 10 | docker-compose up -d 11 | 12 | logs: 13 | docker-compose logs -f 14 | 15 | stop: 16 | docker-compose down 17 | 18 | cleanup: 19 | docker-compose down --rmi all --volumes --remove-orphans 20 | 21 | -------------------------------------------------------------------------------- /assets/architecture-overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/assets/architecture-overview.jpg -------------------------------------------------------------------------------- /assets/basta-spring-2023/BASTA! Spring 2023 - Workshop - Cloud-Native - 1.5 - print.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/assets/basta-spring-2023/BASTA! Spring 2023 - Workshop - Cloud-Native - 1.5 - print.pdf -------------------------------------------------------------------------------- /assets/basta-spring-2023/README.md: -------------------------------------------------------------------------------- 1 | ## Links from the full-day workshop 2 | - https://www.thinktecture.com/wissen 3 | 4 | - https://www.thinktecture.com/thinktects/thorsten-hans/ 5 | 6 | - https://www.thinktecture.com/webinare/real-world-docker-images-fuer-dot-net/ 7 | 8 | - https://opentelemetry.io/ 9 | 10 | - https://grafana.com/oss/loki/ 11 | 12 | - https://www.jaegertracing.io/ 13 | 14 | - https://zipkin.io/ 15 | 16 | - https://grafana.com/grafana 17 | 18 | - https://prometheus.io/ 19 | 20 | - https://cloudevents.io/ 21 | 22 | - https://dapr.io/ -------------------------------------------------------------------------------- /charts/authentication/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/authentication/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | kind: ConfigMap 2 | apiVersion: v1 3 | metadata: 4 | name: authentication 5 | data: 6 | authority: {{ include "authority" . | quote }} 7 | -------------------------------------------------------------------------------- /charts/authentication/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "authentication.fullname" . }} 6 | labels: 7 | {{- include "authentication.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "authentication.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/authentication/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "authentication.fullname" . }} 5 | labels: 6 | {{- include "authentication.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-authn 12 | protocol: TCP 13 | selector: 14 | {{- include "authentication.selectorLabels" . | nindent 4 }} 15 | -------------------------------------------------------------------------------- /charts/authentication/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "authentication.serviceAccountName" . }} 6 | labels: 7 | {{- include "authentication.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/authentication/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "authentication.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "authentication.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "authentication.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/fake-smtp/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/fake-smtp/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: fake-smtp 3 | description: Helm chart to deploy fake-smtp 4 | type: application 5 | version: 0.1.0 6 | appVersion: "1.16.0" 7 | -------------------------------------------------------------------------------- /charts/fake-smtp/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | FakeSMTP has been installed to Namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/fake-smtp/templates/service-smtp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: fakesmtp-smtp 5 | labels: 6 | {{- include "fake-smtp.labels" . | nindent 4 }} 7 | spec: 8 | type: ClusterIP 9 | ports: 10 | - port: 5025 11 | targetPort: smtp 12 | protocol: TCP 13 | name: smtp 14 | selector: 15 | {{- include "fake-smtp.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/fake-smtp/templates/service-web.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: fakesmtp-web 5 | labels: 6 | {{- include "fake-smtp.labels" . | nindent 4 }} 7 | spec: 8 | type: ClusterIP 9 | ports: 10 | - port: 5080 11 | targetPort: web 12 | protocol: TCP 13 | name: web 14 | selector: 15 | {{- include "fake-smtp.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/fake-smtp/values.yaml: -------------------------------------------------------------------------------- 1 | nameOverride: "" 2 | fullnameOverride: "" 3 | podAnnotations: {} 4 | podSecurityContext: {} 5 | securityContext: {} 6 | 7 | ingress: 8 | enabled: true 9 | className: "nginx" 10 | annotations: 11 | cert-manager.io/cluster-issuer: letsencrypt-prod 12 | # kubernetes.io/tls-acme: "true" 13 | hosts: 14 | - host: cn-fakesmtp.thinktecture-demos.com 15 | paths: 16 | - path: / 17 | pathType: Prefix 18 | tls: 19 | - secretName: fakesmtptls 20 | hosts: 21 | - cn-fakesmtp.thinktecture-demos.com 22 | -------------------------------------------------------------------------------- /charts/gateway/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/gateway/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: gateway 3 | description: Gateway Helm Chart 4 | type: application 5 | version: 0.1.0 6 | appVersion: "1.16.0" 7 | -------------------------------------------------------------------------------- /charts/gateway/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "gateway.fullname" . }} 6 | labels: 7 | {{- include "gateway.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "gateway.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/gateway/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "gateway.fullname" . }} 5 | labels: 6 | {{- include "gateway.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-gateway 12 | protocol: TCP 13 | selector: 14 | {{- include "gateway.selectorLabels" . | nindent 4 }} 15 | -------------------------------------------------------------------------------- /charts/gateway/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "gateway.serviceAccountName" . }} 6 | labels: 7 | {{- include "gateway.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/gateway/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "gateway.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "gateway.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "gateway.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/notification/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/notification/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | NotificationService has been installed in Namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/notification/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "notification.fullname" . }} 6 | labels: 7 | {{- include "notification.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "notification.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/notification/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "notification.fullname" . }} 5 | labels: 6 | {{- include "notification.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-noti 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "notification.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/notification/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "notification.serviceAccountName" . }} 6 | labels: 7 | {{- include "notification.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/notification/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "notification.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "notification.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "notification.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/order-monitor-client/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/order-monitor-client/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | Order Monitor Client has been installed in Namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/order-monitor-client/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "order-monitor-client.fullname" . }} 6 | labels: 7 | {{- include "order-monitor-client.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "order-monitor-client.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/order-monitor-client/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "order-monitor-client.fullname" . }} 5 | labels: 6 | {{- include "order-monitor-client.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-omc 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "order-monitor-client.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/order-monitor-client/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "order-monitor-client.serviceAccountName" . }} 6 | labels: 7 | {{- include "order-monitor-client.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/order-monitor-client/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "order-monitor-client.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "order-monitor-client.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "order-monitor-client.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/order-monitor-service/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/order-monitor-service/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | OrderMonitorService has been installed to namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/order-monitor-service/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "order-monitor-service.fullname" . }} 6 | labels: 7 | {{- include "order-monitor-service.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "order-monitor-service.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/order-monitor-service/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "order-monitor-service.fullname" . }} 5 | labels: 6 | {{- include "order-monitor-service.labels" . | nindent 4 }} 7 | spec: 8 | type: ClusterIP 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "order-monitor-service.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/order-monitor-service/values.yaml: -------------------------------------------------------------------------------- 1 | environment: Production 2 | disableConsoleLog: false 3 | image: 4 | repository: nginx 5 | tag: "alpine" 6 | 7 | container: 8 | port: 5000 9 | resources: 10 | requests: 11 | cpu: 100m 12 | memory: 64Mi 13 | limits: 14 | cpu: 100m 15 | memory: 96Mi 16 | 17 | annotations: 18 | dapr.io/enabled: "true" 19 | dapr.io/app-id: "ordermonitorservice" 20 | dapr.io/app-port: "5000" 21 | dapr.io/config: "cloud-native-sample" 22 | prometheus.io/scrape: 'true' 23 | prometheus.io/port: '5000' 24 | 25 | nameOverride: "" 26 | fullnameOverride: "" 27 | 28 | service: 29 | port: 80 30 | 31 | replicas: 1 32 | autoscaling: 33 | enabled: false 34 | minReplicas: 1 35 | maxReplicas: 100 36 | targetCPUUtilizationPercentage: 80 37 | # targetMemoryUtilizationPercentage: 80 38 | -------------------------------------------------------------------------------- /charts/orders/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/orders/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: orders 3 | description: A Helm chart for Kubernetes 4 | type: application 5 | version: 0.1.0 6 | appVersion: "1.16.0" 7 | -------------------------------------------------------------------------------- /charts/orders/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | OrdersService has been installed in Namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/orders/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "orders.fullname" . }} 6 | labels: 7 | {{- include "orders.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "orders.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/orders/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "orders.fullname" . }} 5 | labels: 6 | {{- include "orders.labels" . | nindent 4 }} 7 | spec: 8 | type: ClusterIP 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-orders 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "orders.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/orders/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ include "orders.serviceAccountName" . }} 5 | labels: 6 | {{- include "orders.labels" . | nindent 4 }} 7 | -------------------------------------------------------------------------------- /charts/orders/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "orders.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "orders.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "orders.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/orders/values.yaml: -------------------------------------------------------------------------------- 1 | replicaCount: 1 2 | exposePrometheusMetrics: true 3 | image: 4 | repository: nginx 5 | pullPolicy: IfNotPresent 6 | tag: alpine 7 | 8 | containerPort: 5000 9 | nameOverride: "" 10 | fullnameOverride: "" 11 | 12 | serviceAccount: 13 | # The name of the service account to use. 14 | # If not set and create is true, a name is generated using the fullname template 15 | name: "" 16 | 17 | podAnnotations: 18 | dapr.io/enabled: "true" 19 | dapr.io/app-id: "orders" 20 | dapr.io/app-port: "5000" 21 | dapr.io/config: "cloud-native-sample" 22 | prometheus.io/scrape: 'true' 23 | prometheus.io/port: '5000' 24 | 25 | podSecurityContext: {} 26 | # fsGroup: 2000 27 | 28 | securityContext: {} 29 | # capabilities: 30 | # drop: 31 | # - ALL 32 | # readOnlyRootFilesystem: true 33 | # runAsNonRoot: true 34 | # runAsUser: 1000 35 | 36 | service: 37 | port: 80 38 | 39 | resources: 40 | requests: 41 | cpu: 200m 42 | memory: 128Mi 43 | limits: 44 | cpu: 250m 45 | memory: 256Mi 46 | 47 | autoscaling: 48 | enabled: false 49 | minReplicas: 1 50 | maxReplicas: 100 51 | targetCPUUtilizationPercentage: 80 52 | -------------------------------------------------------------------------------- /charts/price-drop-notifier/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/price-drop-notifier/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: price-drop-notifier 3 | description: A Helm chart for Kubernetes 4 | type: application 5 | version: 0.1.0 6 | appVersion: "1.16.0" 7 | -------------------------------------------------------------------------------- /charts/price-drop-notifier/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | PriceDropNotifier Service has been provisioned to Namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/price-drop-notifier/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "price-drop-notifier.fullname" . }} 6 | labels: 7 | {{- include "price-drop-notifier.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "price-drop-notifier.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/price-drop-notifier/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "price-drop-notifier.fullname" . }} 5 | labels: 6 | {{- include "price-drop-notifier.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-pdropnot 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "price-drop-notifier.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/price-drop-notifier/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "price-drop-notifier.serviceAccountName" . }} 6 | labels: 7 | {{- include "price-drop-notifier.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/price-drop-notifier/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "price-drop-notifier.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "price-drop-notifier.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "price-drop-notifier.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/price-watcher/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/price-watcher/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: price-watcher 3 | description: A Helm chart for Kubernetes 4 | type: application 5 | version: 0.1.0 6 | appVersion: "1.16.0" 7 | -------------------------------------------------------------------------------- /charts/price-watcher/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | PriceWatcher Service has been provisioned to Namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/price-watcher/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "price-watcher.fullname" . }} 6 | labels: 7 | {{- include "price-watcher.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "price-watcher.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/price-watcher/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "price-watcher.fullname" . }} 5 | labels: 6 | {{- include "price-watcher.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-pwatcher 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "price-watcher.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/price-watcher/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "price-watcher.serviceAccountName" . }} 6 | labels: 7 | {{- include "price-watcher.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/price-watcher/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "price-watcher.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "price-watcher.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "price-watcher.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/products/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/products/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: products 3 | description: A Helm chart for Kubernetes 4 | type: application 5 | version: 0.1.0 6 | appVersion: "1.16.0" 7 | -------------------------------------------------------------------------------- /charts/products/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | ProductsService has been installed in Namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/products/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "products.fullname" . }} 6 | labels: 7 | {{- include "products.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "products.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/products/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "products.fullname" . }} 5 | labels: 6 | {{- include "products.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-products 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "products.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/products/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "products.serviceAccountName" . }} 6 | labels: 7 | {{- include "products.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/products/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "products.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "products.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "products.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/shipping/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/shipping/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | ShippingService has been installed in Namespace {{ .Release.Namespace }} 2 | -------------------------------------------------------------------------------- /charts/shipping/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "shipping.fullname" . }} 6 | labels: 7 | {{- include "shipping.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "shipping.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/shipping/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "shipping.fullname" . }} 5 | labels: 6 | {{- include "shipping.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http-shipping 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "shipping.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/shipping/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "shipping.serviceAccountName" . }} 6 | labels: 7 | {{- include "shipping.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/shipping/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "shipping.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "shipping.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "shipping.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /docker-compose-configs/alertmanager/alertmanager.yml: -------------------------------------------------------------------------------- 1 | global: 2 | 3 | route: 4 | receiver: 'mail' 5 | group_wait: 10s 6 | group_interval: 2m 7 | repeat_interval: 3h 8 | group_by: [ alertname ] 9 | 10 | receivers: 11 | - name: 'mail' 12 | email_configs: 13 | - smarthost: 'smtp:5025' 14 | auth_username: 'cnadmin' 15 | auth_password: 'somepassword' 16 | require_tls: false 17 | from: 'alert@cn.thinktecture-demos.com' 18 | to: 'thorsten.hans@thinktecture.com' 19 | -------------------------------------------------------------------------------- /docker-compose-configs/dapr/components/binding-smtp-price-dropped.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: email 5 | spec: 6 | type: bindings.smtp 7 | version: v1 8 | metadata: 9 | - name: host 10 | value: "smtp" 11 | - name: port 12 | value: "5025" 13 | - name: user 14 | value: "cnadmin" 15 | - name: password 16 | value: "somepassword" 17 | - name: skipTLSVerify 18 | value: true 19 | - name: emailFrom 20 | value: "price-drop@cn.thinktecture.com" 21 | -------------------------------------------------------------------------------- /docker-compose-configs/dapr/components/pubsub-rabbitmq-orders.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: orders 5 | namespace: default 6 | spec: 7 | type: pubsub.rabbitmq 8 | version: v1 9 | metadata: 10 | - name: host 11 | value: "amqp://rabbitmq:5672" 12 | - name: enableDeadLetter 13 | value: true 14 | - name: exchangeKind 15 | value: topic 16 | - name: deletedWhenUnused 17 | value: false 18 | - name: deliveryMode 19 | value: 2 20 | -------------------------------------------------------------------------------- /docker-compose-configs/dapr/components/pubsub-rabbitmq-pricedrops.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: pricedrops 5 | namespace: default 6 | spec: 7 | type: pubsub.rabbitmq 8 | version: v1 9 | metadata: 10 | - name: host 11 | value: "amqp://rabbitmq:5672" 12 | - name: enableDeadLetter 13 | value: true 14 | - name: exchangeKind 15 | value: topic 16 | - name: deletedWhenUnused 17 | value: false 18 | - name: deliveryMode 19 | value: 2 20 | -------------------------------------------------------------------------------- /docker-compose-configs/dapr/components/subscription-new-orders.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: sub-new-orders 5 | spec: 6 | topic: new_orders 7 | route: /orders 8 | pubsubname: orders 9 | scopes: 10 | - shipping 11 | -------------------------------------------------------------------------------- /docker-compose-configs/dapr/components/subscription-price-dropped.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: sub-pricedrop-notifications 5 | spec: 6 | topic: notifications 7 | route: /pricedrops/notify 8 | pubsubname: pricedrops 9 | scopes: 10 | - pricedropnotifier 11 | -------------------------------------------------------------------------------- /docker-compose-configs/dapr/components/subscription-processed-orders.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: sub-processed-orders 5 | spec: 6 | topic: processed_orders 7 | route: /orders/processed 8 | pubsubname: orders 9 | scopes: 10 | - notification 11 | -------------------------------------------------------------------------------- /docker-compose-configs/dapr/config.yml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Configuration 3 | metadata: 4 | name: tracing 5 | namespace: default 6 | spec: 7 | tracing: 8 | samplingRate: "1" 9 | zipkin: 10 | endpointAddress: "http://zipkin:9411/api/v2/spans" 11 | -------------------------------------------------------------------------------- /docker-compose-configs/grafana/dashboards/cn-dashboard-provider.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'CN dashboard provider' 5 | disableDeletion: true 6 | updateIntervalSeconds: 1000 7 | allowUiUpdates: true 8 | options: 9 | path: /etc/grafana/provisioning/dashboards 10 | foldersFromFilesStructure: false 11 | -------------------------------------------------------------------------------- /docker-compose-configs/grafana/datasources/all.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | datasources: 3 | - name: Prometheus 4 | type: prometheus 5 | access: proxy 6 | url: http://prometheus:9090 7 | editable: true 8 | - name: Zipkin 9 | type: zipkin 10 | access: proxy 11 | url: http://zipkin:9411 12 | editable: true 13 | - name: Loki 14 | type: loki 15 | access: proxy 16 | url: http://loki:3100 17 | editable: true 18 | -------------------------------------------------------------------------------- /docker-compose-configs/prometheus/cn-alerting-rules.yml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: cloud-native-sample 3 | rules: 4 | - alert: HighOrderVolume 5 | expr: increase(cloud_native_sample_orders_created[1m]) > 5 6 | for: 30s 7 | labels: 8 | severity: high 9 | annotations: 10 | summary: High order Volume detected 11 | description: We've detected a high order volume on cn.thinktecture-demos.com ("{{ $value }}"). 12 | -------------------------------------------------------------------------------- /docker-compose-configs/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | rule_files: 2 | - /etc/prometheus/cn-alerting-rules.yml 3 | alerting: 4 | alertmanagers: 5 | - static_configs: 6 | - targets: ["alertmanager:9093"] 7 | scrape_configs: 8 | - job_name: cadvisor 9 | scrape_interval: 5s 10 | static_configs: 11 | - targets: 12 | - cadvisor:8080 13 | - job_name: apps 14 | scrape_interval: 10s 15 | static_configs: 16 | - targets: 17 | - gateway:5000 18 | - products:5000 19 | - orders:5000 20 | - shipping:5000 21 | - notification:5000 22 | -------------------------------------------------------------------------------- /iac/.gitignore: -------------------------------------------------------------------------------- 1 | dev.sh 2 | -------------------------------------------------------------------------------- /iac/azure-secrets/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc 35 | -------------------------------------------------------------------------------- /iac/azure-secrets/_.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~>1.3.1" 3 | required_providers { 4 | azurerm = { 5 | source = "hashicorp/azurerm" 6 | version = "~>3.21.0" 7 | } 8 | 9 | kubernetes = { 10 | source = "hashicorp/kubernetes" 11 | version = ">= 2.12.1" 12 | } 13 | } 14 | backend "azurerm" { 15 | storage_account_name = "sattcnsample2022" 16 | container_name = "iac" 17 | key = "az-secrets.terraform.tfstate" 18 | use_azuread_auth = true 19 | } 20 | } 21 | 22 | provider "azurerm" { 23 | features {} 24 | } 25 | 26 | provider "kubernetes" { 27 | } 28 | -------------------------------------------------------------------------------- /iac/azure-secrets/databases.tf: -------------------------------------------------------------------------------- 1 | resource "kubernetes_secret" "database" { 2 | metadata { 3 | name = "databases" 4 | namespace = "cloud-native-sample" 5 | } 6 | 7 | data = { 8 | products = "Server=tcp:${var.sql_server_fqdn},1433;Database=cnproducts;User ID=${var.sql_username};Password=${var.sql_password};Trusted_Connection=False;Encrypt=True;MultipleActiveResultSets=True;" 9 | orders = "Server=tcp:${var.sql_server_fqdn},1433;Database=cnorders;User ID=${var.sql_username};Password=${var.sql_password};Trusted_Connection=False;Encrypt=True;MultipleActiveResultSets=True;" 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /iac/azure-secrets/servicebus.tf: -------------------------------------------------------------------------------- 1 | resource "kubernetes_secret" "servicebus" { 2 | metadata { 3 | name = "az-servicebus" 4 | namespace = "cloud-native-sample" 5 | } 6 | data = { 7 | "connectionString" = var.azure_servicebus_connection_string 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /iac/azure-secrets/variables.tf: -------------------------------------------------------------------------------- 1 | variable "sql_server_fqdn" { 2 | type = string 3 | description = "The FQDN of the SQL Server" 4 | } 5 | 6 | variable "sql_username" { 7 | type = string 8 | description = "The username for the SQL Server" 9 | } 10 | 11 | variable "sql_password" { 12 | type = string 13 | sensitive = true 14 | description = "The password for the SQL Server" 15 | } 16 | 17 | variable "azure_servicebus_connection_string" { 18 | type = string 19 | sensitive = true 20 | description = "The connection string for the Azure Service Bus" 21 | } 22 | -------------------------------------------------------------------------------- /iac/azure/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc 35 | -------------------------------------------------------------------------------- /iac/azure/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.3.1 2 | -------------------------------------------------------------------------------- /iac/azure/_.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~>1.3.1" 3 | required_providers { 4 | azurerm = { 5 | source = "hashicorp/azurerm" 6 | version = "~>3.21.0" 7 | } 8 | azuread = { 9 | source = "hashicorp/azuread" 10 | version = "~> 2.15.0" 11 | } 12 | } 13 | backend "azurerm" { 14 | storage_account_name = "sattcnsample2022" 15 | container_name = "iac" 16 | key = "cnsample.terraform.tfstate" 17 | use_azuread_auth = true 18 | } 19 | } 20 | provider "azurerm" { 21 | features { 22 | 23 | } 24 | } 25 | 26 | provider "azuread" { 27 | } 28 | -------------------------------------------------------------------------------- /iac/azure/azure-ad.tf: -------------------------------------------------------------------------------- 1 | resource "azuread_group" "k8s_admins" { 2 | display_name = "Kubernetes Administrators (Cloud-Native Sample)" 3 | mail_enabled = true 4 | mail_nickname = "Kubernetes-Administrators-CNSample" 5 | security_enabled = true 6 | types = ["Unified"] 7 | 8 | owners = [ 9 | data.azuread_client_config.current.object_id, 10 | data.azuread_user.cw.object_id, 11 | data.azuread_user.thh.object_id, 12 | ] 13 | 14 | behaviors = ["HideGroupInOutlook"] 15 | theme = "Teal" 16 | lifecycle { 17 | ignore_changes = [ 18 | owners, members 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /iac/azure/datasources.tf: -------------------------------------------------------------------------------- 1 | data "azurerm_client_config" "current" {} 2 | 3 | data "azuread_client_config" "current" {} 4 | 5 | data "azuread_user" "cw" { 6 | user_principal_name = "christian.weyer@thinktecture.com" 7 | } 8 | 9 | data "azuread_user" "thh" { 10 | user_principal_name = "thorsten.hans@thinktecture.com" 11 | } 12 | 13 | data "azurerm_kubernetes_service_versions" "latest" { 14 | location = var.location 15 | include_preview = false 16 | } 17 | -------------------------------------------------------------------------------- /iac/azure/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | default_tags = { 3 | "com.thinktecture.project" = "Cloud-Native Sample" 4 | "com.thinktecture.owner" = "thorsten.hans@thinktecture.com" 5 | } 6 | tags = merge(local.default_tags, var.custom_tags) 7 | } 8 | -------------------------------------------------------------------------------- /iac/azure/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "main" { 2 | name = "rg-cloud-native-sample-${terraform.workspace}" 3 | location = var.location 4 | tags = local.tags 5 | } 6 | -------------------------------------------------------------------------------- /iac/azure/monitor.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_log_analytics_workspace" "main" { 2 | name = "law-cloud-native-sample" 3 | resource_group_name = azurerm_resource_group.main.name 4 | location = azurerm_resource_group.main.location 5 | 6 | daily_quota_gb = 2 7 | retention_in_days = 90 8 | sku = "PerGB2018" 9 | tags = local.tags 10 | } 11 | 12 | resource "azurerm_application_insights" "main" { 13 | name = "appinsights-cloud-native-sample" 14 | location = azurerm_resource_group.main.location 15 | resource_group_name = azurerm_resource_group.main.name 16 | workspace_id = azurerm_log_analytics_workspace.main.id 17 | application_type = "web" 18 | tags = local.tags 19 | } 20 | -------------------------------------------------------------------------------- /iac/azure/redis.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_redis_cache" "redis" { 2 | name = "redis-cloud-native-sample-${terraform.workspace}" 3 | resource_group_name = azurerm_resource_group.main.name 4 | location = var.location 5 | capacity = 1 6 | family = "C" 7 | sku_name = "Basic" 8 | tags = local.tags 9 | } 10 | -------------------------------------------------------------------------------- /iac/azure/servicebus.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_servicebus_namespace" "main" { 2 | name = "sbn-cloud-native-sample" 3 | resource_group_name = azurerm_resource_group.main.name 4 | location = azurerm_resource_group.main.location 5 | sku = "Standard" 6 | tags = local.tags 7 | } 8 | 9 | 10 | resource "azurerm_servicebus_topic" "new_orders" { 11 | name = "new_orders" 12 | namespace_id = azurerm_servicebus_namespace.main.id 13 | } 14 | 15 | resource "azurerm_servicebus_topic" "processed_orders" { 16 | name = "processed_orders" 17 | namespace_id = azurerm_servicebus_namespace.main.id 18 | } 19 | 20 | resource "azurerm_servicebus_topic" "notifications" { 21 | name = "notifications" 22 | namespace_id = azurerm_servicebus_namespace.main.id 23 | } 24 | -------------------------------------------------------------------------------- /iac/azure/sql.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_mssql_server" "main" { 2 | name = "sql-cloud-native-sample-${terraform.workspace}" 3 | resource_group_name = azurerm_resource_group.main.name 4 | location = var.location 5 | version = "12.0" 6 | administrator_login = var.sql_server_admin_user 7 | administrator_login_password = var.sql_server_admin_password 8 | minimum_tls_version = "1.2" 9 | tags = local.tags 10 | } 11 | 12 | resource "azurerm_mssql_database" "products" { 13 | name = "cnproducts" 14 | server_id = azurerm_mssql_server.main.id 15 | sku_name = "S0" 16 | zone_redundant = false 17 | 18 | tags = local.tags 19 | } 20 | 21 | resource "azurerm_mssql_database" "orders" { 22 | name = "cnorders" 23 | server_id = azurerm_mssql_server.main.id 24 | sku_name = "S0" 25 | zone_redundant = false 26 | 27 | tags = local.tags 28 | } 29 | -------------------------------------------------------------------------------- /iac/azure/variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | type = string 3 | default = "germanywestcentral" 4 | description = "Azure Region used to deploy all services to" 5 | } 6 | 7 | variable "custom_tags" { 8 | type = map(string) 9 | default = {} 10 | description = "Custom Azure Tags assigned to Azure resources" 11 | } 12 | 13 | variable "acr_name" { 14 | type = string 15 | description = "Desired ACR instance name" 16 | } 17 | 18 | variable "acr_token_name" { 19 | type = string 20 | description = "Desired ACR token name" 21 | } 22 | 23 | variable "sql_server_admin_user" { 24 | type = string 25 | default = "cnadmin" 26 | description = "Username for the SQL Server admin user" 27 | } 28 | 29 | variable "sql_server_admin_password" { 30 | type = string 31 | sensitive = true 32 | description = "Password for the SQL Server admin user" 33 | } 34 | -------------------------------------------------------------------------------- /iac/dapr/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc 35 | -------------------------------------------------------------------------------- /iac/dapr/_.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~>1.3.1" 3 | required_providers { 4 | azurerm = { 5 | source = "hashicorp/azurerm" 6 | version = "~>3.21.0" 7 | } 8 | helm = { 9 | source = "hashicorp/helm" 10 | version = ">= 2.6.0" 11 | } 12 | 13 | kubernetes = { 14 | source = "hashicorp/kubernetes" 15 | version = ">= 2.12.1" 16 | } 17 | } 18 | backend "azurerm" { 19 | storage_account_name = "sattcnsample2022" 20 | container_name = "iac" 21 | key = "dapr.terraform.tfstate" 22 | use_azuread_auth = true 23 | } 24 | } 25 | 26 | provider "azurerm" { 27 | features {} 28 | } 29 | 30 | provider "helm" { 31 | kubernetes { 32 | } 33 | } 34 | 35 | provider "kubernetes" { 36 | } 37 | -------------------------------------------------------------------------------- /iac/dapr/datasources.tf: -------------------------------------------------------------------------------- 1 | data "kubernetes_namespace" "app" { 2 | metadata { 3 | name = var.app_namespace 4 | } 5 | } 6 | 7 | data "kubernetes_service_v1" "zipkin" { 8 | metadata { 9 | name = var.zipkin_config.name 10 | namespace = var.zipkin_config.namespace 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iac/dapr/manifests/email.yml: -------------------------------------------------------------------------------- 1 | kind: Component 2 | apiVersion: dapr.io/v1alpha1 3 | metadata: 4 | name: email 5 | namespace: cloud-native-sample 6 | spec: 7 | type: bindings.twilio.sendgrid 8 | version: v1 9 | metadata: 10 | - name: emailFrom 11 | value: "christian.weyer@thinktecture.com" 12 | - name: apiKey 13 | secretKeyRef: 14 | key: apiKey 15 | name: twilio 16 | -------------------------------------------------------------------------------- /iac/dapr/manifests/orders_azservicebus.yml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: orders 5 | namespace: cloud-native-sample 6 | spec: 7 | type: pubsub.azure.servicebus 8 | version: v1 9 | metadata: 10 | - name: connectionString 11 | secretKeyRef: 12 | key: connectionString 13 | name: az-servicebus 14 | -------------------------------------------------------------------------------- /iac/dapr/manifests/orders_rabbitmq.yml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: orders 5 | namespace: cloud-native-sample 6 | spec: 7 | type: pubsub.rabbitmq 8 | version: v1 9 | metadata: 10 | - name: host 11 | secretKeyRef: 12 | key: connectionString 13 | name: rabbitmq 14 | - name: enableDeadLetter 15 | value: true 16 | - name: exchangeKind 17 | value: topic 18 | - name: deletedWhenUnused 19 | value: false 20 | - name: deliveryMode 21 | value: "2" 22 | -------------------------------------------------------------------------------- /iac/dapr/manifests/pricedrops_azservicebus.yml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: pricedrops 5 | namespace: cloud-native-sample 6 | spec: 7 | type: pubsub.azure.servicebus 8 | version: v1 9 | metadata: 10 | - name: connectionString 11 | secretKeyRef: 12 | key: connectionString 13 | name: az-servicebus 14 | -------------------------------------------------------------------------------- /iac/dapr/manifests/pricedrops_rabbitmq.yml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: pricedrops 5 | namespace: cloud-native-sample 6 | spec: 7 | type: pubsub.rabbitmq 8 | version: v1 9 | metadata: 10 | - name: host 11 | secretKeyRef: 12 | key: connectionString 13 | name: rabbitmq 14 | - name: enableDeadLetter 15 | value: true 16 | - name: exchangeKind 17 | value: topic 18 | - name: deletedWhenUnused 19 | value: false 20 | - name: deliveryMode 21 | value: "2" 22 | -------------------------------------------------------------------------------- /iac/dapr/variables.tf: -------------------------------------------------------------------------------- 1 | variable "dapr_config_sample_rate" { 2 | type = number 3 | default = 1 4 | } 5 | 6 | variable "dapr_version" { 7 | type = string 8 | default = "1.10.5" 9 | } 10 | variable "dapr_api_auth_token" { 11 | type = string 12 | sensitive = true 13 | } 14 | variable "app_namespace" { 15 | type = string 16 | default = "cloud-native-sample" 17 | } 18 | 19 | 20 | variable "zipkin_config" { 21 | type = object({ 22 | name = string 23 | namespace = string 24 | }) 25 | default = { 26 | name = "zipkin" 27 | namespace = "zipkin" 28 | } 29 | } 30 | 31 | variable "message_broker" { 32 | type = string 33 | default = "azservicebus" 34 | validation { 35 | condition = contains(["azservicebus", "rabbitmq"], var.message_broker) 36 | error_message = "Message broker must be either azservicebus or rabbitmq" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iac/format-all.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # simple script to re-format all terraform sources 4 | echo "Work, Work! 🚧" 5 | 6 | terraform fmt -recursive 7 | 8 | echo "All done! ✅" 9 | 10 | 11 | -------------------------------------------------------------------------------- /iac/kubernetes/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc 35 | -------------------------------------------------------------------------------- /iac/kubernetes/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.3.1 2 | -------------------------------------------------------------------------------- /iac/kubernetes/_.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~>1.3.1" 3 | required_providers { 4 | azurerm = { 5 | source = "hashicorp/azurerm" 6 | version = "~>3.21.0" 7 | } 8 | helm = { 9 | source = "hashicorp/helm" 10 | version = ">= 2.6.0" 11 | } 12 | 13 | kubernetes = { 14 | source = "hashicorp/kubernetes" 15 | version = ">= 2.12.1" 16 | } 17 | } 18 | backend "azurerm" { 19 | storage_account_name = "sattcnsample2022" 20 | container_name = "iac" 21 | key = "k8s.terraform.tfstate" 22 | use_azuread_auth = true 23 | } 24 | } 25 | 26 | provider "azurerm" { 27 | features {} 28 | } 29 | 30 | provider "helm" { 31 | kubernetes { 32 | } 33 | } 34 | 35 | provider "kubernetes" { 36 | } 37 | -------------------------------------------------------------------------------- /iac/kubernetes/app.tf: -------------------------------------------------------------------------------- 1 | resource "kubernetes_namespace" "app" { 2 | 3 | metadata { 4 | name = "cloud-native-sample" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /iac/kubernetes/grafana.tf: -------------------------------------------------------------------------------- 1 | resource "helm_release" "grafana" { 2 | name = "cn-grafana" 3 | repository = "https://grafana.github.io/helm-charts" 4 | chart = "grafana" 5 | timeout = 600 6 | namespace = "grafana" 7 | create_namespace = true 8 | 9 | values = [ 10 | templatefile("${path.module}/values/grafana.yml", { 11 | prometheus_endpoint = local.prometheus_endpoint 12 | loki_endpoint = local.loki_query_endpoint 13 | zipkin_endpoint = local.zipkin_endpoint 14 | }), 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /iac/kubernetes/ingress.tf: -------------------------------------------------------------------------------- 1 | resource "helm_release" "ingress" { 2 | name = "cn-ingress" 3 | repository = "https://kubernetes.github.io/ingress-nginx" 4 | chart = "ingress-nginx" 5 | timeout = 600 6 | namespace = "ingress" 7 | create_namespace = true 8 | 9 | values = [ 10 | file("${path.module}/values/ingress.yml"), 11 | ] 12 | } 13 | 14 | data "kubernetes_service" "ing" { 15 | metadata { 16 | name = "${helm_release.ingress.name}-ingress-nginx-controller" 17 | namespace = helm_release.ingress.namespace 18 | } 19 | depends_on = [ 20 | helm_release.ingress 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /iac/kubernetes/loki.tf: -------------------------------------------------------------------------------- 1 | resource "helm_release" "grafana_loki" { 2 | name = "loki" 3 | repository = "https://grafana.github.io/helm-charts" 4 | chart = "loki-distributed" 5 | namespace = "loki" 6 | create_namespace = true 7 | 8 | values = [ 9 | templatefile("${path.module}/values/loki.yml", { 10 | storage_account_name = var.loki_storage_account_name 11 | storage_account_key = var.loki_storage_account_key 12 | }), 13 | ] 14 | } 15 | 16 | locals { 17 | loki_distributor_endpoint = "http://${helm_release.grafana_loki.name}-loki-distributed-distributor.${helm_release.grafana_loki.namespace}.svc.cluster.local:3100/loki/api/v1/push" 18 | loki_query_endpoint = "http://${helm_release.grafana_loki.name}loki-distributed-query-frontend.${helm_release.grafana_loki.namespace}.svc.cluster.local:3100" 19 | 20 | } 21 | -------------------------------------------------------------------------------- /iac/kubernetes/prometheus.tf: -------------------------------------------------------------------------------- 1 | resource "helm_release" "prometheus" { 2 | name = "prometheus" 3 | repository = "https://prometheus-community.github.io/helm-charts" 4 | chart = "prometheus" 5 | namespace = "prometheus" 6 | create_namespace = true 7 | 8 | values = [file("${path.module}/values/prometheus.yml")] 9 | } 10 | 11 | locals { 12 | prometheus_endpoint = "http://${helm_release.prometheus.name}-server.${helm_release.prometheus.namespace}.svc.cluster.local:80" 13 | } 14 | -------------------------------------------------------------------------------- /iac/kubernetes/promtail.tf: -------------------------------------------------------------------------------- 1 | resource "helm_release" "grafana_promtail" { 2 | name = "promtail" 3 | repository = "https://grafana.github.io/helm-charts" 4 | chart = "promtail" 5 | namespace = "promtail" 6 | create_namespace = true 7 | 8 | values = [ 9 | file("${path.module}/values/promtail.yml") 10 | ] 11 | set { 12 | name = "config.clients[0].url" 13 | value = local.loki_distributor_endpoint 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /iac/kubernetes/values/grafana.yml: -------------------------------------------------------------------------------- 1 | ingress: 2 | enabled: true 3 | ingressClassName: nginx 4 | annotations: 5 | cert-manager.io/cluster-issuer: "letsencrypt-prod" 6 | hosts: 7 | - cn-grafana.thinktecture-demos.com 8 | tls: 9 | - secretName: grafanatls 10 | hosts: 11 | - cn-grafana.thinktecture-demos.com 12 | 13 | deploymentStrategy: 14 | type: Recreate 15 | 16 | persistence: 17 | enabled: true 18 | 19 | datasources: 20 | datasources.yaml: 21 | apiVersion: 1 22 | datasources: 23 | - name: Prometheus (Cloud-Native Sample) 24 | uid: cnprometheus 25 | type: prometheus 26 | url: ${prometheus_endpoint} 27 | access: proxy 28 | isDefault: true 29 | editable: true 30 | - name: Grafana Loki (Cloud-Native Sample) 31 | uid: cnloki 32 | type: loki 33 | url: ${loki_endpoint} 34 | access: proxy 35 | editable: true 36 | - name: Zipkin (Cloud-Native Sample) 37 | uid: cnzipkin 38 | type: zipkin 39 | url: ${zipkin_endpoint} 40 | access: proxy 41 | editable: true 42 | 43 | -------------------------------------------------------------------------------- /iac/kubernetes/values/ingress.yml: -------------------------------------------------------------------------------- 1 | controller: 2 | metrics: 3 | enabled: true 4 | port: 10254 5 | portName: metrics 6 | service: 7 | annotations: 8 | prometheus.io/scrape: "true" 9 | prometheus.io/port: "10254" 10 | servicePort: 10254 11 | 12 | service: 13 | annotations: 14 | service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: "/healthz" 15 | -------------------------------------------------------------------------------- /iac/kubernetes/values/loki.yml: -------------------------------------------------------------------------------- 1 | nameOverride: null 2 | fullnameOverride: null 3 | imagePullSecrets: [] 4 | loki: 5 | # -- Check https://grafana.com/docs/loki/latest/configuration/#schema_config for more info on how to configure schemas 6 | schemaConfig: 7 | configs: 8 | - from: 2022-01-01 9 | store: boltdb-shipper 10 | object_store: azure 11 | schema: v11 12 | index: 13 | prefix: loki_index_ 14 | period: 24h 15 | 16 | # -- Check https://grafana.com/docs/loki/latest/configuration/#storage_config for more info on how to configure storages 17 | storageConfig: 18 | boltdb_shipper: 19 | shared_store: azure 20 | cache_ttl: 24h 21 | azure: 22 | account_name: ${storage_account_name} 23 | account_key: ${storage_account_key} 24 | container_name: loki 25 | use_managed_identity: false 26 | 27 | ingester: 28 | persistence: 29 | enabled: true 30 | size: 100Gi 31 | querier: 32 | persistence: 33 | enabled: true 34 | size: 100Gi 35 | tableManager: 36 | enabled: false 37 | compactor: 38 | enabled: true 39 | persistence: 40 | enabled: true 41 | size: 50Gi 42 | -------------------------------------------------------------------------------- /iac/kubernetes/values/prometheus.yml: -------------------------------------------------------------------------------- 1 | serverFiles: 2 | alerting_rules.yml: 3 | groups: 4 | - name: cloud-native-sample 5 | rules: 6 | - alert: HighOrderVolume 7 | expr: increase(cloud_native_sample_orders_created[5m]) > 5 8 | for: 1m 9 | labels: 10 | severity: high 11 | annotations: 12 | summary: High order Volume detected 13 | description: We've detected a high order volume on cn.thinktecture-demos.com ("{{ $value }}"). 14 | alertmanager: 15 | enabled: true 16 | config: 17 | route: 18 | receiver: "mail" 19 | group_wait: 10s 20 | group_interval: 2m 21 | repeat_interval: 3h 22 | group_by: [alertname] 23 | 24 | receivers: 25 | - name: "mail" 26 | email_configs: 27 | - smarthost: "fakesmtp-smtp.fake-smtp.svc.cluster.local:5025" 28 | auth_username: "cnadmin" 29 | auth_password: "somepassword" 30 | require_tls: false 31 | from: "alert@cn.thinktecture-demos.com" 32 | to: "thorsten.hans@thinktecture.com" 33 | -------------------------------------------------------------------------------- /iac/kubernetes/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | 3 | variable "dns_zone" { 4 | type = object({ 5 | deploy_dns = bool 6 | name = string 7 | resource_group_name = string 8 | }) 9 | 10 | default = { 11 | deploy_dns = true 12 | name = "thinktecture-demos.com" 13 | resource_group_name = "rg-research" 14 | } 15 | } 16 | 17 | variable "rabbitmq_user" { 18 | type = string 19 | default = "cloudnativesample" 20 | } 21 | 22 | variable "rabbitmq_password" { 23 | type = string 24 | default = "Some_Secret_Password$" 25 | description = "Overwrite this default value using a GitHub Secret" 26 | sensitive = true 27 | } 28 | 29 | variable "loki_storage_account_name" { 30 | type = string 31 | default = "sattcnsampleloki" 32 | } 33 | 34 | variable "loki_storage_account_key" { 35 | type = string 36 | sensitive = true 37 | } 38 | 39 | -------------------------------------------------------------------------------- /iac/state/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc 35 | -------------------------------------------------------------------------------- /iac/state/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.2.8 2 | -------------------------------------------------------------------------------- /iac/state/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | default_tags = { 3 | "com.thinktecture.project" = "Cloud-Native Sample" 4 | "com.thinktecture.owner" = "thorsten.hans@thinktecture.com" 5 | } 6 | tags = merge(local.default_tags, var.custom_tags) 7 | } 8 | -------------------------------------------------------------------------------- /iac/state/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~>1.2.8" 3 | required_providers { 4 | azurerm = { 5 | source = "hashicorp/azurerm" 6 | version = "~>3.21.0" 7 | } 8 | } 9 | } 10 | provider "azurerm" { 11 | features { 12 | 13 | } 14 | } 15 | data "azurerm_client_config" "current" {} 16 | 17 | resource "azurerm_resource_group" "iac" { 18 | name = "rg-cloud-native-sample-iac" 19 | location = var.location 20 | 21 | tags = local.tags 22 | } 23 | 24 | -------------------------------------------------------------------------------- /iac/state/storage.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "azurerm_storage_account" "iac" { 3 | name = "sattcnsample2022" 4 | resource_group_name = azurerm_resource_group.iac.name 5 | location = azurerm_resource_group.iac.location 6 | 7 | account_kind = "StorageV2" 8 | account_replication_type = "LRS" 9 | account_tier = "Standard" 10 | 11 | is_hns_enabled = false 12 | blob_properties { 13 | change_feed_enabled = false 14 | versioning_enabled = true 15 | } 16 | 17 | } 18 | 19 | resource "azurerm_storage_container" "iac" { 20 | name = "iac" 21 | storage_account_name = azurerm_storage_account.iac.name 22 | container_access_type = "private" 23 | } 24 | 25 | resource "azurerm_role_assignment" "storage_contrib" { 26 | scope = azurerm_storage_account.iac.id 27 | role_definition_name = "Storage Blob Data Contributor" 28 | principal_id = data.azurerm_client_config.current.object_id 29 | } 30 | -------------------------------------------------------------------------------- /iac/state/variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | type = string 3 | default = "germanywestcentral" 4 | description = "Azure Region used to deploy all services to" 5 | } 6 | 7 | variable "custom_tags" { 8 | type = map(string) 9 | default = {} 10 | description = "Custom Azure Tags assigned to Azure resources" 11 | } 12 | -------------------------------------------------------------------------------- /iac/validate-all.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # simple script to validate all terraform sources 4 | echo "Work, Work! 🚧" 5 | 6 | echo " - 🔎 Validating top level module" 7 | cd azure 8 | terraform validate 9 | cd .. 10 | 11 | cd state 12 | echo " - 🔎 Validating state module" 13 | terraform validate 14 | cd .. 15 | 16 | cd kubernetes 17 | echo " - 🔎 Validating Kubernetes module" 18 | terraform validate 19 | cd .. 20 | 21 | echo "All done! ✅" 22 | 23 | 24 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=Cloud-Native-Sample 2 | -------------------------------------------------------------------------------- /src/AdminCli/AdminCli.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/AdminCli/CliCommands/CliCommandsModule.cs: -------------------------------------------------------------------------------- 1 | using AdminCli.CliCommands.Environment; 2 | using AdminCli.CliCommands.Products; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace AdminCli.CliCommands; 6 | 7 | public static class CliCommandsModule 8 | { 9 | public static IServiceCollection AddCliCommands(this IServiceCollection services) => 10 | services.AddEnvironmentCommands() 11 | .AddProductCommands(); 12 | } 13 | -------------------------------------------------------------------------------- /src/AdminCli/CliCommands/CommandConfigurationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using McMaster.Extensions.CommandLineUtils; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace AdminCli.CliCommands; 6 | 7 | public readonly record struct CommandConfigurationContext(CommandLineApplication Config, 8 | IServiceProvider Container) 9 | { 10 | public CommandConfigurationContext ConfigureCommand(string commandName) 11 | where TCommand : class, ICliCommand 12 | { 13 | 14 | var command = Container.GetRequiredService(); 15 | Config.Command(commandName, 16 | subConfig => command.ConfigureCommand(subConfig)); 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/AdminCli/CliCommands/Environment/EnvironmentCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using McMaster.Extensions.CommandLineUtils; 3 | 4 | namespace AdminCli.CliCommands.Environment; 5 | 6 | public sealed class EnvironmentCommand : ICliCommand 7 | { 8 | public EnvironmentCommand(IServiceProvider container) => Container = container; 9 | 10 | private IServiceProvider Container { get; } 11 | 12 | public void ConfigureCommand(CommandLineApplication config) 13 | { 14 | config.Description = "Allows you to show or set the targeted environment"; 15 | var context = new CommandConfigurationContext(config, Container); 16 | context.ConfigureCommand("show"); 17 | context.ConfigureCommand("set"); 18 | 19 | config.OnExecute(() => config.ShowHelp()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AdminCli/CliCommands/Environment/EnvironmentCommandsModule.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace AdminCli.CliCommands.Environment; 4 | 5 | public static class EnvironmentCommandsModule 6 | { 7 | public static IServiceCollection AddEnvironmentCommands(this IServiceCollection services) => 8 | services.AddSingleton() 9 | .AddSingleton() 10 | .AddSingleton(); 11 | } 12 | -------------------------------------------------------------------------------- /src/AdminCli/CliCommands/Environment/ShowEnvironmentCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AdminCli.Configuration; 3 | using McMaster.Extensions.CommandLineUtils; 4 | 5 | namespace AdminCli.CliCommands.Environment; 6 | 7 | public sealed class ShowEnvironmentCommand : ICliCommand 8 | { 9 | public ShowEnvironmentCommand(IConfigurationManager configurationManager) => 10 | ConfigurationManager = configurationManager; 11 | 12 | private IConfigurationManager ConfigurationManager { get; } 13 | 14 | public void ConfigureCommand(CommandLineApplication config) 15 | { 16 | config.Description = "Outputs the gateway and identity server URLs you are currently targeting"; 17 | config.OnExecute(Execute); 18 | } 19 | 20 | public void Execute() 21 | { 22 | var appSettings = ConfigurationManager.CurrentSettings; 23 | Console.Write("You are targeting gateway "); 24 | Console.Write(appSettings.GatewayUrl); 25 | Console.Write(" and identity server "); 26 | Console.WriteLine(appSettings.IdentityServerSettings.ServerUrl); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/AdminCli/CliCommands/ICliCommand.cs: -------------------------------------------------------------------------------- 1 | using McMaster.Extensions.CommandLineUtils; 2 | 3 | namespace AdminCli.CliCommands; 4 | 5 | public interface ICliCommand 6 | { 7 | void ConfigureCommand(CommandLineApplication config); 8 | } 9 | -------------------------------------------------------------------------------- /src/AdminCli/CliCommands/Products/ProductCommandsModule.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace AdminCli.CliCommands.Products; 4 | 5 | public static class ProductCommandsModule 6 | { 7 | public static IServiceCollection AddProductCommands(this IServiceCollection services) => 8 | services.AddSingleton() 9 | .AddSingleton() 10 | .AddSingleton(); 11 | } 12 | -------------------------------------------------------------------------------- /src/AdminCli/CliCommands/Products/ProductsCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using McMaster.Extensions.CommandLineUtils; 3 | 4 | namespace AdminCli.CliCommands.Products; 5 | 6 | public sealed class ProductsCommand : ICliCommand 7 | { 8 | public ProductsCommand(IServiceProvider serviceProvider) => ServiceProvider = serviceProvider; 9 | 10 | private IServiceProvider ServiceProvider { get; } 11 | 12 | public void ConfigureCommand(CommandLineApplication config) 13 | { 14 | config.Description = "Allows you to list products or issue a price drop"; 15 | var context = new CommandConfigurationContext(config, ServiceProvider); 16 | context.ConfigureCommand("list"); 17 | context.ConfigureCommand("drop-price"); 18 | 19 | config.OnExecute(() => config.ShowHelp()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AdminCli/Configuration/AppSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace AdminCli.Configuration; 5 | 6 | public sealed class AppSettings 7 | { 8 | public const string DefaultLocalGatewayUrl = "http://localhost:5000"; 9 | public const string DefaultCloudGatewayUrl = "https://cn.thinktecture-demos.com/api"; 10 | public IdentityServerSettings IdentityServerSettings { get; set; } = new (); 11 | public string GatewayUrl { get; set; } = DefaultLocalGatewayUrl; 12 | public int AccessTokenValidToThreshold { get; set; } = 10; 13 | public TokenInfo? CurrentTokenInfo { get; set; } 14 | 15 | public bool TryGetAccessToken([NotNullWhen(true)] out string? accessToken) 16 | { 17 | var now = DateTime.UtcNow; 18 | if (CurrentTokenInfo is null || 19 | CurrentTokenInfo.ValidTo.AddSeconds(-AccessTokenValidToThreshold) < now) 20 | { 21 | accessToken = default; 22 | return false; 23 | } 24 | 25 | accessToken = CurrentTokenInfo.AccessToken; 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/AdminCli/Configuration/ConfigurationModule.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using Microsoft.AspNetCore.DataProtection; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace AdminCli.Configuration; 6 | 7 | public static class ConfigurationModule 8 | { 9 | public static IServiceCollection AddConfiguration(this IServiceCollection services) => 10 | services.AddSingleton(container => 11 | { 12 | var dataProtector = container.GetRequiredService(); 13 | var jsonSerializerOptions = container.GetRequiredService(); 14 | return SecureFileConfigurationManager.Create(dataProtector, jsonSerializerOptions); 15 | }) 16 | .AddSingleton( 17 | container => 18 | container.GetRequiredService() 19 | .CreateProtector("admin-cli") 20 | ) 21 | .AddDataProtection() 22 | .Services; 23 | } 24 | -------------------------------------------------------------------------------- /src/AdminCli/Configuration/IConfigurationManager.cs: -------------------------------------------------------------------------------- 1 | namespace AdminCli.Configuration; 2 | 3 | public interface IConfigurationManager 4 | { 5 | AppSettings CurrentSettings { get; } 6 | void StoreAppSettingsSecurely(AppSettings appSettings); 7 | } 8 | -------------------------------------------------------------------------------- /src/AdminCli/Configuration/IdentityServerSettings.cs: -------------------------------------------------------------------------------- 1 | namespace AdminCli.Configuration; 2 | 3 | public sealed class IdentityServerSettings 4 | { 5 | public const string DefaultLocalServerUrl = "http://localhost:5009"; 6 | public const string DefaultCloudServerUrl = "https://cn.thinktecture-demos.com/login"; 7 | 8 | // We shouldn't normally expose sensitive data here, 9 | // but for the sake of simplicity, we just keep the 10 | // credentials in here. 11 | public string ServerUrl { get; set; } = DefaultLocalServerUrl; 12 | public string ClientId { get; set; } = "admin-cli"; 13 | public string ClientSecret { get; set; } = "46E345BC-9C72-4694-8BFF-27AA6BB2B6A2"; 14 | public string Scopes { get; set; } = "admin sample"; 15 | } 16 | -------------------------------------------------------------------------------- /src/AdminCli/Configuration/TokenInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdminCli.Configuration; 4 | 5 | public record TokenInfo(string AccessToken, DateTime ValidTo); 6 | -------------------------------------------------------------------------------- /src/AdminCli/HttpAccess/HttpAccessModule.cs: -------------------------------------------------------------------------------- 1 | using System.IdentityModel.Tokens.Jwt; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace AdminCli.HttpAccess; 5 | 6 | public static class HttpAccessModule 7 | { 8 | public static IServiceCollection AddHttpAccess(this IServiceCollection services) => 9 | services.AddHttpClient() 10 | .AddSingleton(new JwtSecurityTokenHandler()) 11 | .AddSingleton() 12 | .AddSingleton(); 13 | } 14 | -------------------------------------------------------------------------------- /src/AdminCli/HttpAccess/IHttpService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace AdminCli.HttpAccess; 5 | 6 | public interface IHttpService 7 | { 8 | Task GetAsync(string relativeUrl, CancellationToken cancellationToken = default); 9 | Task PostAsync(string relativeUrl, T content, CancellationToken cancellationToken = default); 10 | } 11 | -------------------------------------------------------------------------------- /src/AdminCli/Infrastructure/DependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using AdminCli.CliCommands; 2 | using AdminCli.Configuration; 3 | using AdminCli.HttpAccess; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace AdminCli.Infrastructure; 8 | 9 | public static class DependencyInjection 10 | { 11 | public static ServiceProvider CreateContainer(ILoggerFactory loggerFactory) => 12 | new ServiceCollection().ConfigureServices(loggerFactory) 13 | .BuildServiceProvider(); 14 | 15 | private static IServiceCollection ConfigureServices(this IServiceCollection services, 16 | ILoggerFactory loggerFactory) => 17 | services.AddLogging(loggerFactory) 18 | .AddJsonSerializerOptions() 19 | .AddConfiguration() 20 | .AddHttpAccess() 21 | .AddCommandLineApp() 22 | .AddCliCommands(); 23 | } 24 | -------------------------------------------------------------------------------- /src/AdminCli/Infrastructure/Json.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace AdminCli.Infrastructure; 5 | 6 | public static class Json 7 | { 8 | public static IServiceCollection AddJsonSerializerOptions(this IServiceCollection services) => 9 | services.AddSingleton(CreateOptions()); 10 | 11 | private static JsonSerializerOptions CreateOptions() => 12 | new () 13 | { 14 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase, 15 | WriteIndented = false, 16 | PropertyNameCaseInsensitive = true 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/AuthenticationService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/AuthenticationService/ApiScopesProvider.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | using IdentityModel; 3 | namespace AuthenticationService; 4 | 5 | public static class ApiScopesProvider 6 | { 7 | 8 | public static IEnumerable GetAll() 9 | { 10 | var sample = new ApiScope("sample"); 11 | var admin = new ApiScope("admin"); 12 | 13 | sample.UserClaims.Add(JwtClaimTypes.Name); 14 | 15 | return new ApiScope[] 16 | { 17 | sample, 18 | admin 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AuthenticationService/CustomProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using OpenTelemetry; 3 | 4 | namespace AuthenticationService; 5 | 6 | internal sealed class CustomProcessor : BaseProcessor 7 | { 8 | public override void OnEnd(Activity activity) 9 | { 10 | if (IsHealthOrMetricsEndpoint(activity.DisplayName)) 11 | { 12 | activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; 13 | } 14 | } 15 | 16 | private static bool IsHealthOrMetricsEndpoint(string displayName) 17 | { 18 | if (string.IsNullOrEmpty(displayName)) 19 | { 20 | return false; 21 | } 22 | return displayName.StartsWith("/healthz/") || 23 | displayName.StartsWith("/metrics"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AuthenticationService/IdentityResourcesProvider.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | 3 | namespace AuthenticationService; 4 | 5 | public static class IdentityResourcesProvider { 6 | 7 | public static IEnumerable GetAll => 8 | new IdentityResource[] 9 | { 10 | new IdentityResources.OpenId(), 11 | new IdentityResources.Profile(), 12 | }; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.Account.AccessDeniedModel 3 | @{ 4 | } 5 |
6 |
7 |

Access Denied

8 |

You do not have permission to access that resource.

9 |
10 |
-------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/AccessDenied.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | 3 | namespace authn.Pages.Account; 4 | 5 | public class AccessDeniedModel : PageModel 6 | { 7 | public void OnGet() 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/Login/InputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | namespace authn.Pages.Login; 8 | 9 | public class InputModel 10 | { 11 | [Required] 12 | public string Username { get; set; } 13 | 14 | [Required] 15 | public string Password { get; set; } 16 | 17 | public bool RememberLogin { get; set; } 18 | 19 | public string ReturnUrl { get; set; } 20 | 21 | public string Button { get; set; } 22 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/Login/LoginOptions.cs: -------------------------------------------------------------------------------- 1 | namespace authn.Pages.Login; 2 | 3 | public class LoginOptions 4 | { 5 | public static bool AllowLocalLogin = true; 6 | public static bool AllowRememberLogin = true; 7 | public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); 8 | public static string InvalidCredentialsErrorMessage = "Invalid username or password"; 9 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/Login/ViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | namespace authn.Pages.Login; 5 | 6 | public class ViewModel 7 | { 8 | public bool AllowRememberLogin { get; set; } = true; 9 | public bool EnableLocalLogin { get; set; } = true; 10 | 11 | public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); 12 | public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); 13 | 14 | public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; 15 | public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; 16 | 17 | public class ExternalProvider 18 | { 19 | public string DisplayName { get; set; } 20 | public string AuthenticationScheme { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/Logout/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.Logout.Index 3 | 4 |
5 |
6 |

Logout

7 |

Would you like to logout of IdentityServer?

8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 |
17 |
-------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/Logout/LoggedOut.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.Logout.LoggedOut 3 | 4 |
5 |

6 | Logout 7 | You are now logged out 8 |

9 | 10 | @if (Model.View.PostLogoutRedirectUri != null) 11 | { 12 |
13 | Click here to return to the 14 | @Model.View.ClientName application. 15 |
16 | } 17 | 18 | @if (Model.View.SignOutIframeUrl != null) 19 | { 20 | 21 | } 22 |
23 | 24 | @section scripts 25 | { 26 | @if (Model.View.AutomaticRedirectAfterSignOut) 27 | { 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/Logout/LoggedOutViewModel.cs: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) Duende Software. All rights reserved. 3 | // See LICENSE in the project root for license information. 4 | 5 | 6 | namespace authn.Pages.Logout; 7 | 8 | public class LoggedOutViewModel 9 | { 10 | public string PostLogoutRedirectUri { get; set; } 11 | public string ClientName { get; set; } 12 | public string SignOutIframeUrl { get; set; } 13 | public bool AutomaticRedirectAfterSignOut { get; set; } 14 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Account/Logout/LogoutOptions.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace authn.Pages.Logout; 3 | 4 | public class LogoutOptions 5 | { 6 | public static bool ShowLogoutPrompt = true; 7 | public static bool AutomaticRedirectAfterSignOut = true; 8 | } 9 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Ciba/ConsentOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | 5 | namespace authn.Pages.Ciba; 6 | 7 | public class ConsentOptions 8 | { 9 | public static bool EnableOfflineAccess = true; 10 | public static string OfflineAccessDisplayName = "Offline Access"; 11 | public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; 12 | 13 | public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; 14 | public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; 15 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Ciba/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.Ciba.IndexModel 3 | @{ 4 | } 5 | 6 |
7 |
8 | @if (Model.LoginRequest.Client.LogoUri != null) 9 | { 10 | 11 | } 12 |

13 | @Model.LoginRequest.Client.ClientName 14 | is requesting your permission 15 |

16 | 17 |

18 | Verify that this identifier matches what the client is displaying: 19 | @Model.LoginRequest.BindingMessage 20 |

21 | 22 |

23 | Do you wish to continue? 24 |

25 |
26 | Yes, Continue 27 |
28 | 29 |
30 |
31 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Ciba/InputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | namespace authn.Pages.Ciba; 5 | 6 | public class InputModel 7 | { 8 | public string Button { get; set; } 9 | public IEnumerable ScopesConsented { get; set; } 10 | public string Id { get; set; } 11 | public string Description { get; set; } 12 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Ciba/ViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | namespace authn.Pages.Ciba; 5 | 6 | public class ViewModel 7 | { 8 | public string ClientName { get; set; } 9 | public string ClientUrl { get; set; } 10 | public string ClientLogoUrl { get; set; } 11 | 12 | public string BindingMessage { get; set; } 13 | 14 | public IEnumerable IdentityScopes { get; set; } 15 | public IEnumerable ApiScopes { get; set; } 16 | } 17 | 18 | public class ScopeViewModel 19 | { 20 | public string Name { get; set; } 21 | public string Value { get; set; } 22 | public string DisplayName { get; set; } 23 | public string Description { get; set; } 24 | public bool Emphasize { get; set; } 25 | public bool Required { get; set; } 26 | public bool Checked { get; set; } 27 | public IEnumerable Resources { get; set; } 28 | } 29 | 30 | public class ResourceViewModel 31 | { 32 | public string Name { get; set; } 33 | public string DisplayName { get; set; } 34 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Consent/ConsentOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | 5 | namespace authn.Pages.Consent; 6 | 7 | public class ConsentOptions 8 | { 9 | public static bool EnableOfflineAccess = true; 10 | public static string OfflineAccessDisplayName = "Offline Access"; 11 | public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; 12 | 13 | public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; 14 | public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; 15 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Consent/InputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | namespace authn.Pages.Consent; 5 | 6 | public class InputModel 7 | { 8 | public string Button { get; set; } 9 | public IEnumerable ScopesConsented { get; set; } 10 | public bool RememberConsent { get; set; } = true; 11 | public string ReturnUrl { get; set; } 12 | public string Description { get; set; } 13 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Consent/ViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | namespace authn.Pages.Consent; 5 | 6 | public class ViewModel 7 | { 8 | public string ClientName { get; set; } 9 | public string ClientUrl { get; set; } 10 | public string ClientLogoUrl { get; set; } 11 | public bool AllowRememberConsent { get; set; } 12 | 13 | public IEnumerable IdentityScopes { get; set; } 14 | public IEnumerable ApiScopes { get; set; } 15 | } 16 | 17 | public class ScopeViewModel 18 | { 19 | public string Name { get; set; } 20 | public string Value { get; set; } 21 | public string DisplayName { get; set; } 22 | public string Description { get; set; } 23 | public bool Emphasize { get; set; } 24 | public bool Required { get; set; } 25 | public bool Checked { get; set; } 26 | public IEnumerable Resources { get; set; } 27 | } 28 | 29 | public class ResourceViewModel 30 | { 31 | public string Name { get; set; } 32 | public string DisplayName { get; set; } 33 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Device/DeviceOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | 5 | namespace authn.Pages.Device; 6 | 7 | public class DeviceOptions 8 | { 9 | public static bool EnableOfflineAccess = true; 10 | public static string OfflineAccessDisplayName = "Offline Access"; 11 | public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; 12 | 13 | public static readonly string InvalidUserCode = "Invalid user code"; 14 | public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; 15 | public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; 16 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Device/InputModel.cs: -------------------------------------------------------------------------------- 1 | namespace authn.Pages.Device; 2 | 3 | public class InputModel 4 | { 5 | public string Button { get; set; } 6 | public IEnumerable ScopesConsented { get; set; } 7 | public bool RememberConsent { get; set; } = true; 8 | public string ReturnUrl { get; set; } 9 | public string Description { get; set; } 10 | public string UserCode { get; set; } 11 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Device/Success.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.Device.SuccessModel 3 | @{ 4 | } 5 | 6 | 7 |
8 |
9 |

Success

10 |

You have successfully authorized the device

11 |
12 |
13 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Device/Success.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | 4 | namespace authn.Pages.Device; 5 | 6 | [SecurityHeaders] 7 | [Authorize] 8 | public class SuccessModel : PageModel 9 | { 10 | public void OnGet() 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Device/ViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace authn.Pages.Device; 2 | 3 | public class ViewModel 4 | { 5 | public string ClientName { get; set; } 6 | public string ClientUrl { get; set; } 7 | public string ClientLogoUrl { get; set; } 8 | public bool AllowRememberConsent { get; set; } 9 | 10 | public IEnumerable IdentityScopes { get; set; } 11 | public IEnumerable ApiScopes { get; set; } 12 | } 13 | 14 | public class ScopeViewModel 15 | { 16 | public string Value { get; set; } 17 | public string DisplayName { get; set; } 18 | public string Description { get; set; } 19 | public bool Emphasize { get; set; } 20 | public bool Required { get; set; } 21 | public bool Checked { get; set; } 22 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Diagnostics/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | using Microsoft.AspNetCore.Authorization; 5 | 6 | namespace authn.Pages.Diagnostics; 7 | 8 | [SecurityHeaders] 9 | [Authorize] 10 | public class Index : PageModel 11 | { 12 | public ViewModel View { get; set; } 13 | 14 | public async Task OnGet() 15 | { 16 | var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; 17 | if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) 18 | { 19 | return NotFound(); 20 | } 21 | 22 | View = new ViewModel(await HttpContext.AuthenticateAsync()); 23 | 24 | return Page(); 25 | } 26 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Diagnostics/ViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityModel; 6 | using Microsoft.AspNetCore.Authentication; 7 | using System.Text; 8 | using System.Text.Json; 9 | 10 | namespace authn.Pages.Diagnostics; 11 | 12 | public class ViewModel 13 | { 14 | public ViewModel(AuthenticateResult result) 15 | { 16 | AuthenticateResult = result; 17 | 18 | if (result.Properties.Items.ContainsKey("client_list")) 19 | { 20 | var encoded = result.Properties.Items["client_list"]; 21 | var bytes = Base64Url.Decode(encoded); 22 | var value = Encoding.UTF8.GetString(bytes); 23 | 24 | Clients = JsonSerializer.Deserialize(value); 25 | } 26 | } 27 | 28 | public AuthenticateResult AuthenticateResult { get; } 29 | public IEnumerable Clients { get; } = new List(); 30 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/ExternalLogin/Callback.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.ExternalLogin.Callback 3 | 4 | @{ 5 | Layout = null; 6 | } 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/ExternalLogin/Challenge.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.ExternalLogin.Challenge 3 | 4 | @{ 5 | Layout = null; 6 | } 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Grants/ViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace authn.Pages.Grants; 2 | 3 | public class ViewModel 4 | { 5 | public IEnumerable Grants { get; set; } 6 | } 7 | 8 | public class GrantViewModel 9 | { 10 | public string ClientId { get; set; } 11 | public string ClientName { get; set; } 12 | public string ClientUrl { get; set; } 13 | public string ClientLogoUrl { get; set; } 14 | public string Description { get; set; } 15 | public DateTime Created { get; set; } 16 | public DateTime? Expires { get; set; } 17 | public IEnumerable IdentityGrantNames { get; set; } 18 | public IEnumerable ApiGrantNames { get; set; } 19 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Home/Error/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.Error.Index 3 | 4 |
5 |
6 |

Error

7 |
8 | 9 |
10 |
11 |
12 | Sorry, there was an error 13 | 14 | @if (Model.View.Error != null) 15 | { 16 | 17 | 18 | : @Model.View.Error.Error 19 | 20 | 21 | 22 | if (Model.View.Error.ErrorDescription != null) 23 | { 24 |
@Model.View.Error.ErrorDescription
25 | } 26 | } 27 |
28 | 29 | @if (Model?.View?.Error?.RequestId != null) 30 | { 31 |
Request Id: @Model.View.Error.RequestId
32 | } 33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Home/Error/ViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Duende Software. All rights reserved. 2 | // See LICENSE in the project root for license information. 3 | 4 | using Duende.IdentityServer.Models; 5 | 6 | namespace authn.Pages.Error; 7 | 8 | public class ViewModel 9 | { 10 | public ViewModel() 11 | { 12 | } 13 | 14 | public ViewModel(string error) 15 | { 16 | Error = new ErrorMessage { Error = error }; 17 | } 18 | 19 | public ErrorMessage Error { get; set; } 20 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | 5 | namespace authn.Pages.Home; 6 | 7 | [AllowAnonymous] 8 | public class Index : PageModel 9 | { 10 | public string Version; 11 | 12 | public void OnGet() 13 | { 14 | Version = typeof(Duende.IdentityServer.Hosting.IdentityServerMiddleware).Assembly.GetCustomAttribute()?.InformationalVersion.Split('+').First(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Redirect/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model authn.Pages.Redirect.IndexModel 3 | @{ 4 | } 5 | 6 |
7 |
8 |

You are now being returned to the application

9 |

Once complete, you may close this tab.

10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Redirect/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | 5 | namespace authn.Pages.Redirect; 6 | 7 | [AllowAnonymous] 8 | public class IndexModel : PageModel 9 | { 10 | public string RedirectUri { get; set; } 11 | 12 | public IActionResult OnGet(string redirectUri) 13 | { 14 | if (!Url.IsLocalUrl(redirectUri)) 15 | { 16 | return RedirectToPage("/Home/Error/Index"); 17 | } 18 | 19 | RedirectUri = redirectUri; 20 | return Page(); 21 | } 22 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Duende IdentityServer 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | @RenderBody() 22 |
23 | 24 | 25 | 26 | 27 | @RenderSection("scripts", required: false) 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Shared/_Nav.cshtml: -------------------------------------------------------------------------------- 1 | @using Duende.IdentityServer.Extensions 2 | @{ 3 | string name = null; 4 | if (!true.Equals(ViewData["signed-out"])) 5 | { 6 | name = Context.User?.GetDisplayName(); 7 | } 8 | } 9 | 10 | 33 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/Shared/_ValidationSummary.cshtml: -------------------------------------------------------------------------------- 1 | @if (ViewContext.ModelState.IsValid == false) 2 | { 3 |
4 | Error 5 |
6 |
7 | } -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using authn.Pages 2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 | -------------------------------------------------------------------------------- /src/AuthenticationService/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/AuthenticationService/Program.cs: -------------------------------------------------------------------------------- 1 | using AuthenticationService.Configuration; 2 | using AuthenticationService.Extensions; 3 | 4 | try 5 | { 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | var cfg = new IdentityServerConfig(); 9 | var cfgSection = builder.Configuration.GetSection(IdentityServerConfig.SectionName); 10 | 11 | if (cfgSection == null || !cfgSection.Exists()) 12 | { 13 | throw new ApplicationException( 14 | $"Could not find Authentication configuration. Please ensure a '{IdentityServerConfig.SectionName}' exists"); 15 | } 16 | 17 | cfgSection.Bind(cfg); 18 | builder.Services.AddSingleton(cfg); 19 | 20 | // logging 21 | builder.ConfigureLogging(cfg); 22 | // traces 23 | builder.ConfigureTracing(cfg); 24 | // metrics 25 | builder.ConfigureMetrics(cfg); 26 | 27 | var app = builder 28 | .ConfigureServices() 29 | .ConfigurePipeline(); 30 | 31 | app.Run(); 32 | } 33 | catch (Exception ex) 34 | { 35 | Console.WriteLine("Unhandled exception" + ex); 36 | } 37 | finally 38 | { 39 | Console.WriteLine("Shut down complete"); 40 | } 41 | -------------------------------------------------------------------------------- /src/AuthenticationService/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SelfHost": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "applicationUrl": "https://localhost:5009", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | .welcome-page .logo { 2 | width: 64px; 3 | } 4 | 5 | .icon-banner { 6 | width: 32px; 7 | } 8 | 9 | .body-container { 10 | margin-top: 60px; 11 | padding-bottom: 40px; 12 | } 13 | 14 | .welcome-page li { 15 | list-style: none; 16 | padding: 4px; 17 | } 18 | 19 | .logged-out-page iframe { 20 | display: none; 21 | width: 0; 22 | height: 0; 23 | } 24 | 25 | .grants-page .card { 26 | margin-top: 20px; 27 | border-bottom: 1px solid lightgray; 28 | } 29 | .grants-page .card .card-title { 30 | font-size: 120%; 31 | font-weight: bold; 32 | } 33 | .grants-page .card .card-title img { 34 | width: 100px; 35 | height: 100px; 36 | } 37 | .grants-page .card label { 38 | font-weight: bold; 39 | } 40 | -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | .welcome-page .logo{width:64px;}.icon-banner{width:32px;}.body-container{margin-top:60px;padding-bottom:40px;}.welcome-page li{list-style:none;padding:4px;}.logged-out-page iframe{display:none;width:0;height:0;}.grants-page .card{margin-top:20px;border-bottom:1px solid #d3d3d3;}.grants-page .card .card-title{font-size:120%;font-weight:bold;}.grants-page .card .card-title img{width:100px;height:100px;}.grants-page .card label{font-weight:bold;} -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/css/site.scss: -------------------------------------------------------------------------------- 1 | .welcome-page { 2 | .logo { 3 | width: 64px; 4 | } 5 | } 6 | 7 | .icon-banner { 8 | width: 32px; 9 | } 10 | 11 | .body-container { 12 | margin-top: 60px; 13 | padding-bottom: 40px; 14 | } 15 | 16 | .welcome-page { 17 | li { 18 | list-style: none; 19 | padding: 4px; 20 | } 21 | } 22 | 23 | .logged-out-page { 24 | iframe { 25 | display: none; 26 | width: 0; 27 | height: 0; 28 | } 29 | } 30 | 31 | .grants-page { 32 | .card { 33 | margin-top: 20px; 34 | border-bottom: 1px solid lightgray; 35 | 36 | .card-title { 37 | img { 38 | width: 100px; 39 | height: 100px; 40 | } 41 | 42 | font-size: 120%; 43 | font-weight: bold; 44 | } 45 | 46 | label { 47 | font-weight: bold; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/duende-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/src/AuthenticationService/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/js/signin-redirect.js: -------------------------------------------------------------------------------- 1 | window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); 2 | -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/js/signout-redirect.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function () { 2 | var a = document.querySelector("a.PostLogoutRedirectUri"); 3 | if (a) { 4 | window.location = a.href; 5 | } 6 | }); 7 | -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/lib/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/src/AuthenticationService/wwwroot/lib/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/lib/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/src/AuthenticationService/wwwroot/lib/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/lib/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/src/AuthenticationService/wwwroot/lib/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/AuthenticationService/wwwroot/lib/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/src/AuthenticationService/wwwroot/lib/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/CloudNativeSample.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /src/Gateway/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/Gateway/Configuration/GatewayConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging.Console; 2 | 3 | namespace Gateway.Configuration; 4 | 5 | public class GatewayConfiguration 6 | { 7 | public const string SectionName = "Gateway"; 8 | public string ConsoleFormatterName { get; set; } = ConsoleFormatterNames.Json; 9 | public bool DisableConsoleLog { get; set; } 10 | public string ZipkinEndpoint { get; set; } 11 | public bool ExposePrometheusMetrics { get; set; } 12 | public string ApplicationInsightsConnectionString { get; set; } 13 | public string ConfigSection { get; set; } = "ReverseProxy"; 14 | public string[] CorsOrigins { get; set; } = { "http://localhost:5005" }; 15 | 16 | public int LimitRequestsPerMinute { get; set; } = 3000; 17 | 18 | public string[] DaprServiceNames { get; set; } = { 19 | Constants.ProductsRouteName, 20 | Constants.OrdersRouteName 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/Gateway/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Gateway; 2 | 3 | public class Constants 4 | { 5 | public const string ProductsRouteName = "products"; 6 | public const string OrdersRouteName = "orders"; 7 | public const string CorsPolicyName = "GatewayPolicy"; 8 | public const string ServiceName = "Gateway"; 9 | public const string HttpClientName = "ordermonitor"; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/Gateway/CustomMetrics.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.Metrics; 2 | using System.Reflection; 3 | 4 | namespace Gateway; 5 | 6 | public class CustomMetrics 7 | { 8 | 9 | public static readonly Meter Default = new Meter("Gateway", 10 | Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0"); 11 | 12 | public static readonly Counter ProxiedRequests = Default.CreateCounter("cloud_native_sample_proxied_requests", 13 | description: "Number of proxied requests"); 14 | } 15 | -------------------------------------------------------------------------------- /src/Gateway/CustomProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using OpenTelemetry; 3 | 4 | namespace Gateway; 5 | 6 | internal sealed class CustomProcessor : BaseProcessor 7 | { 8 | public override void OnEnd(Activity activity) 9 | { 10 | if (IsHealthOrMetricsEndpoint(activity.DisplayName)) 11 | { 12 | activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; 13 | } 14 | } 15 | 16 | private static bool IsHealthOrMetricsEndpoint(string displayName) 17 | { 18 | if (string.IsNullOrEmpty(displayName)) 19 | { 20 | return false; 21 | } 22 | return displayName.StartsWith("/healthz/") || 23 | displayName.StartsWith("/metrics"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Gateway/Features/Cors.cs: -------------------------------------------------------------------------------- 1 | using Gateway.Configuration; 2 | 3 | namespace Gateway.Features; 4 | 5 | public static class Cors 6 | { 7 | public static void Add(WebApplicationBuilder builder, GatewayConfiguration cfg) 8 | { 9 | builder.Services.AddCors(options => 10 | { 11 | options.AddPolicy(Constants.CorsPolicyName, builder => { 12 | builder 13 | .AllowAnyHeader() 14 | .AllowAnyMethod() 15 | .WithOrigins(cfg.CorsOrigins); 16 | }); 17 | 18 | }); 19 | } 20 | 21 | public static void Use(WebApplication app) 22 | { 23 | app.UseCors(Constants.CorsPolicyName); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Gateway/Features/HeaderPropagation.cs: -------------------------------------------------------------------------------- 1 | namespace Gateway.Features; 2 | 3 | public static class HeaderPropagation 4 | { 5 | public static string[] PropagatedHeaders = new [] { "Authorization" }; 6 | public static void Add(WebApplicationBuilder builder) 7 | { 8 | builder.Services.AddHeaderPropagation(options => { 9 | PropagatedHeaders.ToList().ForEach(h=> { 10 | options.Headers.Add(h); 11 | }); 12 | }); 13 | 14 | builder.Services.AddHttpClient(Constants.HttpClientName) 15 | .AddHeaderPropagation(); 16 | } 17 | 18 | public static void Use(WebApplication app) 19 | { 20 | app.UseHeaderPropagation(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Gateway/Features/RateLimiting.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.RateLimiting; 2 | using Gateway.Configuration; 3 | using Microsoft.AspNetCore.RateLimiting; 4 | 5 | namespace Gateway.Features; 6 | 7 | public static class RateLimiting 8 | { 9 | public static void Add(WebApplicationBuilder builder, GatewayConfiguration cfg) 10 | { 11 | builder.Services.AddRateLimiter(options => 12 | { 13 | options.RejectionStatusCode = 429; 14 | options.AddFixedWindowLimiter("CloudNativeSamplePolicy", opt => 15 | { 16 | opt.PermitLimit = cfg.LimitRequestsPerMinute; 17 | opt.Window = TimeSpan.FromMinutes(1); 18 | }); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Gateway/Features/ResponseCompression.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Compression; 2 | using Gateway.Configuration; 3 | using Microsoft.AspNetCore.ResponseCompression; 4 | 5 | namespace Gateway.Features; 6 | 7 | public static class ResponseCompression 8 | { 9 | public static void Add(WebApplicationBuilder builder) 10 | { 11 | builder.Services.AddResponseCompression(options => 12 | { 13 | options.EnableForHttps = true; 14 | options.Providers.Clear(); 15 | options.Providers.Add(new GzipCompressionProvider(new GzipCompressionProviderOptions 16 | { 17 | Level = CompressionLevel.Fastest 18 | })); 19 | }); 20 | } 21 | 22 | public static void Use(WebApplication app) 23 | { 24 | app.UseResponseCompression(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Gateway/Models/OrderMonitorListModel.cs: -------------------------------------------------------------------------------- 1 | namespace Gateway.Models; 2 | 3 | public class OrderMonitorListModel 4 | { 5 | public Guid Id { get; set; } 6 | public string UserId { get; set; } 7 | public IList Positions { get; set; } 8 | } 9 | 10 | public class OrderMonitorPositionModel 11 | { 12 | public Guid ProductId { get; set; } 13 | public string ProductName { get; set; } 14 | public string ProductDescription { get; set; } 15 | public int Quantity { get; set; } 16 | public double ProductPrice { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/Gateway/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:5000", 8 | "sslPort": 44300 9 | } 10 | }, 11 | "profiles": { 12 | "Gateway": { 13 | "commandName": "Project", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "applicationUrl": "http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "launchUrl": "swagger", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Gateway/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Trace", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "Gateway": { 9 | "TraceSystem": "Zipkin", 10 | "TraceEndpoint": "http://zipkin.zipkin.svc.cluster.local:9411/api/v2/spans" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/NotificationService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/NotificationService/Configuration/Authorization.cs: -------------------------------------------------------------------------------- 1 | namespace NotificationService.Configuration; 2 | 3 | public class Authorization 4 | { 5 | public string RequiredClaimName { get; set; } = "scope"; 6 | public string RequiredClaimValue {get;set;} = "sample"; 7 | } 8 | -------------------------------------------------------------------------------- /src/NotificationService/Configuration/IdentityServerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace NotificationService.Configuration; 2 | 3 | public class IdentityServerConfiguration 4 | { 5 | public string Authority {get;set;} 6 | public string MetadataAddress {get;set;} 7 | public bool RequireHttpsMetadata { get; set;} = true; 8 | } 9 | -------------------------------------------------------------------------------- /src/NotificationService/Configuration/NotificationServiceConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging.Console; 2 | 3 | namespace NotificationService.Configuration; 4 | 5 | public class NotificationServiceConfiguration 6 | { 7 | public NotificationServiceConfiguration() 8 | { 9 | IdentityServer = new IdentityServerConfiguration(); 10 | Authorization = new Authorization(); 11 | } 12 | public const string SectionName = "NotificationService"; 13 | public string ConsoleFormatterName { get; set; } = ConsoleFormatterNames.Json; 14 | public bool DisableConsoleLog { get; set; } 15 | public string OnOrderProcessedMethodName { get; set; } = "onOrderProcessed"; 16 | public string ZipkinEndpoint { get; set; } 17 | public bool ExposePrometheusMetrics {get;set;} 18 | public string ApplicationInsightsConnectionString {get;set;} 19 | public IdentityServerConfiguration IdentityServer { get;set; } 20 | public Authorization Authorization { get; set; } 21 | } 22 | -------------------------------------------------------------------------------- /src/NotificationService/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace NotificationService; 2 | 3 | public static class Constants 4 | { 5 | public const string ServiceName = "NotificationService"; 6 | public const string AuthorizationPolicyName = "RequiresApiScope"; 7 | public const string NotificationHubEndpoint = "/notifications/notificationHub"; 8 | } 9 | -------------------------------------------------------------------------------- /src/NotificationService/CustomMetrics.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.Metrics; 2 | using System.Reflection; 3 | 4 | namespace NotificationService; 5 | 6 | public class CustomMetrics 7 | { 8 | public static readonly Meter Default = new Meter("NotificationService", 9 | Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0"); 10 | 11 | public static readonly Counter NotificationsSent = 12 | Default.CreateCounter("cloud_native_sample_notifications_sent", 13 | description: "Number of notifications sent via SignalR"); 14 | } 15 | -------------------------------------------------------------------------------- /src/NotificationService/CustomProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using OpenTelemetry; 3 | 4 | namespace NotificationService; 5 | 6 | internal sealed class CustomProcessor : BaseProcessor 7 | { 8 | public override void OnEnd(Activity activity) 9 | { 10 | if (IsHealthOrMetricsEndpoint(activity.DisplayName)) 11 | { 12 | activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; 13 | } 14 | } 15 | 16 | private static bool IsHealthOrMetricsEndpoint(string displayName) 17 | { 18 | if (string.IsNullOrEmpty(displayName)) 19 | { 20 | return false; 21 | } 22 | return displayName.StartsWith("/healthz/") || 23 | displayName.StartsWith("/metrics"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/NotificationService/Extensions/HttpRequestExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNetCore.Http; 2 | 3 | public static class HttpRequestExtensions 4 | { 5 | 6 | const string DaprApiTokenHeaderName = "dapr-api-token"; 7 | const string DaprApiTokenEnvironmentVariableName = "DAPR_API_TOKEN"; 8 | public static bool HasValidDaprApiToken(this HttpRequest request) 9 | { 10 | if (request.Headers.TryGetValue(DaprApiTokenHeaderName, out var tokens)) 11 | { 12 | var expected = GetDaprApiToken(); 13 | var actual = tokens.FirstOrDefault(); 14 | return expected == actual; 15 | } 16 | return false; 17 | } 18 | 19 | private static string GetDaprApiToken() 20 | { 21 | var token = Environment.GetEnvironmentVariable(DaprApiTokenEnvironmentVariableName); 22 | if (string.IsNullOrEmpty(token)) 23 | { 24 | throw new InvalidOperationException($"{DaprApiTokenEnvironmentVariableName} environment variable is not set"); 25 | } 26 | return token; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/NotificationService/Models/DispatchedOrder.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace NotificationService.Models; 4 | 5 | public class DispatchedOrder 6 | { 7 | [JsonPropertyName("orderId")] 8 | public string OrderId { get; set; } 9 | 10 | [JsonPropertyName("userId")] 11 | public string UserId { get; set; } 12 | [JsonPropertyName("userName")] 13 | public string UserName { get; set; } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/NotificationService/NotificationHub.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using Microsoft.AspNetCore.SignalR; 3 | 4 | namespace NotificationService; 5 | 6 | public class NotificationHub : Hub 7 | { 8 | public override async Task OnConnectedAsync() 9 | { 10 | var subject = Context.User.FindFirst(ClaimTypes.NameIdentifier); 11 | if (subject == null) 12 | { 13 | subject = new Claim(ClaimTypes.NameIdentifier, string.Empty); 14 | } 15 | await Groups.AddToGroupAsync(Context.ConnectionId, subject.Value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/NotificationService/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:5000", 8 | "sslPort": 44304 9 | } 10 | }, 11 | "profiles": { 12 | "NotificationService": { 13 | "commandName": "Project", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "applicationUrl": "http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "launchUrl": "swagger", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/NotificationService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning", 6 | "NotificationService": "Trace" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "NotificationService": { 11 | "OnOrderProcessedMethodName": "onOrderProcessed", 12 | "IdentityServer": { 13 | "Authority": "", 14 | "RequireHttpsMetadata": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch and Debug Standalone Blazor WebAssembly App", 6 | "type": "blazorwasm", 7 | "request": "launch", 8 | "cwd": "${workspaceFolder}" 9 | }, 10 | { 11 | "name": "Docker .NET Core Launch", 12 | "type": "docker", 13 | "request": "launch", 14 | "preLaunchTask": "docker-run: debug", 15 | "netCore": { 16 | "appProject": "${workspaceFolder}/OrderMonitorClient.csproj" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/Authentication/Authentication.razor: -------------------------------------------------------------------------------- 1 | @page "/authentication/{action}" 2 | 3 | @using Microsoft.AspNetCore.Components.WebAssembly.Authentication 4 | 5 | 6 | 7 | @code{ 8 | [Parameter] public string Action { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/Authentication/LoginDisplay.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Authorization 2 | @using Microsoft.AspNetCore.Components.WebAssembly.Authentication 3 | @using Microsoft.AspNetCore.Components.Web 4 | 5 | @inject NavigationManager _navigation 6 | @inject SignOutSessionStateManager _signOutManager 7 | 8 | 9 | 10 | Hello, @context.User.Identity.Name! 11 | 12 | 13 | 14 | Log in 15 | 16 | 17 | 18 | @code{ 19 | private async Task BeginSignOut(MouseEventArgs args) 20 | { 21 | await _signOutManager.SetSignOutState(); 22 | 23 | _navigation.NavigateTo("authentication/logout"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/Authentication/RedirectToLogin.razor: -------------------------------------------------------------------------------- 1 | @inject NavigationManager _navigation 2 | 3 | @code { 4 | protected override void OnInitialized() 5 | { 6 | _navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(_navigation.Uri)}"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/Models/OrderModels.cs: -------------------------------------------------------------------------------- 1 | namespace OrderMonitorClient.Models; 2 | 3 | public class OrderMonitorListModel 4 | { 5 | public Guid Id { get; set; } 6 | public string UserId { get; set; } 7 | public IList Positions { get; set; } 8 | } 9 | 10 | public class OrderMonitorPositionModel 11 | { 12 | public Guid ProductId { get; set; } 13 | public string ProductName { get; set; } 14 | public string ProductDescription { get; set; } 15 | public int Quantity { get; set; } 16 | public double ProductPrice { get; set; } 17 | } 18 | 19 | public class OrderCreateModel 20 | { 21 | public OrderCreateModel() 22 | { 23 | Positions = new List(); 24 | } 25 | public List Positions { get; set; } 26 | } 27 | 28 | public class OrderPositionCreateModel 29 | { 30 | public string ProductId { get; set; } 31 | public int Quantity { get; set; } 32 | } 33 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/Models/ProductModels.cs: -------------------------------------------------------------------------------- 1 | namespace OrderMonitorClient.Models; 2 | 3 | public class ProductListModel 4 | { 5 | public Guid Id { get; set; } 6 | public string Name { get; set; } 7 | public string Description { get; set; } 8 | public double Price { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Order Monitor 4 | 5 | Welcome 👋🏼 6 | The Order Monitor is part of our cloud-native sample application!
You can authenticate using bob:bob.
7 | If you've any questions or feedback, create a corresponding issue here on GitHub. 8 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:5005", 7 | "sslPort": 44398 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "Mudblazor.Template": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 23 | "applicationUrl": "http://localhost:5005", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/OrderMonitorClient/Services/ProductsService.cs: -------------------------------------------------------------------------------- 1 | using OrderMonitorClient.Models; 2 | using System.Net.Http.Json; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Authentication; 4 | 5 | namespace OrderMonitorClient.Services 6 | { 7 | public class ProductsService 8 | { 9 | private readonly IAccessTokenProvider _tokenProvider; 10 | private readonly HttpClient _httpClient; 11 | private readonly string _apiRoot; 12 | 13 | public ProductsService(IAccessTokenProvider tokenProvider, IConfiguration config, HttpClient httpClient) 14 | { 15 | _tokenProvider = tokenProvider; 16 | _apiRoot = config["ApiRoot"]; 17 | _httpClient = httpClient; 18 | } 19 | 20 | public async Task> GetProductsAsync() 21 | { 22 | var products = await _httpClient.GetFromJsonAsync>($"{_apiRoot}/products"); 23 | return products; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Authorization 4 | @using Microsoft.AspNetCore.Components.Authorization 5 | @using Microsoft.AspNetCore.Components.Forms 6 | @using Microsoft.AspNetCore.Components.Routing 7 | @using Microsoft.AspNetCore.Components.Web 8 | @using Microsoft.AspNetCore.Components.Web.Virtualization 9 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 10 | @using Microsoft.JSInterop 11 | @using MudBlazor 12 | @using OrderMonitorClient 13 | @using OrderMonitorClient.Authentication 14 | @using OrderMonitorClient.Shared 15 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; 3 | sendfile on; 4 | default_type application/octet-stream; 5 | 6 | gzip on; 7 | gzip_http_version 1.0; 8 | gzip_disable "MSIE [1-6]\."; 9 | gzip_min_length 256; 10 | gzip_vary on; 11 | gzip_proxied expired no-cache no-store private auth; 12 | gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; 13 | gzip_comp_level 9; 14 | 15 | root /usr/share/nginx/html; 16 | 17 | location / { 18 | add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; 19 | expires -1; 20 | try_files $uri $uri/ /index.html =404; 21 | } 22 | 23 | location /k8s/ { 24 | 25 | } 26 | 27 | location = /index.html { 28 | add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; 29 | expires -1; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/wwwroot/appsettings.docker-compose.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApiRoot": "", 3 | "Oidc": { 4 | "Authority": "http://localhost:5009/", 5 | "MetadataUrl": "http://localhost:5009/.well-known/openid-configuration", 6 | "ClientId": "interactive", 7 | "DefaultScopes": [ 8 | "openid", 9 | "profile", 10 | "sample" 11 | ], 12 | "ResponseType": "code" 13 | }, 14 | "ZipkinUrl": "http://localhost:9411/zipkin/", 15 | "GrafanaUrl": "http://localhost:3000/", 16 | "FakeSmtpUrl": "http://localhost:5080/", 17 | "Logging": { 18 | "LogLevel": { 19 | "Default": "Debug", 20 | "Microsoft.AspNetCore": "Debug" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApiRoot": "", 3 | "Oidc": { 4 | "Authority": "http://localhost:5009/", 5 | "MetadataUrl": "http://localhost:5009/.well-known/openid-configuration", 6 | "ClientId": "interactive", 7 | "DefaultScopes": [ 8 | "openid", 9 | "profile", 10 | "sample" 11 | ], 12 | "ResponseType": "code" 13 | }, 14 | "ZipkinUrl": "http://localhost:9411/zipkin/", 15 | "GrafanaUrl": "http://localhost:3000/", 16 | "FakeSmtpUrl": "http://localhost:5080/", 17 | "Logging": { 18 | "LogLevel": { 19 | "Default": "Debug", 20 | "Microsoft.AspNetCore": "Debug" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/wwwroot/appsettings.k8s.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApiRoot": "/api", 3 | "Oidc": { 4 | "Authority": "https://cn.thinktecture-demos.com/login/", 5 | "MetadataUrl": "https://cn.thinktecture-demos.com/login/.well-known/openid-configuration", 6 | "ClientId": "interactive", 7 | "DefaultScopes": [ 8 | "openid", 9 | "profile", 10 | "sample" 11 | ], 12 | "ResponseType": "code" 13 | }, 14 | "ZipkinUrl": "https://cn-zipkin.thinktecture-demos.com/zipkin/", 15 | "GrafanaUrl": "https://cn-grafana.thinktecture-demos.com/", 16 | "FakeSmtpUrl": "https://cn-fakesmtp.thinktecture-demos.com/", 17 | "Logging": { 18 | "LogLevel": { 19 | "Default": "Debug", 20 | "Microsoft.AspNetCore": "Debug" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/src/OrderMonitorClient/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/OrderMonitorClient/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OrderMonitorClient 8 | 9 | 10 | 11 | 12 | 13 | 14 |
Loading...
15 | 16 |
17 | An unhandled error has occurred. 18 | Reload 19 | 🗙 20 |
21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/OrderMonitorClient/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2018-05-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing" 6 | }, 7 | { 8 | "date": "2018-05-07", 9 | "temperatureC": 14, 10 | "summary": "Bracing" 11 | }, 12 | { 13 | "date": "2018-05-08", 14 | "temperatureC": -13, 15 | "summary": "Freezing" 16 | }, 17 | { 18 | "date": "2018-05-09", 19 | "temperatureC": -16, 20 | "summary": "Balmy" 21 | }, 22 | { 23 | "date": "2018-05-10", 24 | "temperatureC": -2, 25 | "summary": "Chilly" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /src/OrderMonitorService/.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/go 3 | { 4 | "name": "Go", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/go:0-1-bullseye" 7 | 8 | // Features to add to the dev container. More info: https://containers.dev/features. 9 | // "features": {}, 10 | 11 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 12 | // "forwardPorts": [], 13 | 14 | // Use 'postCreateCommand' to run commands after the container is created. 15 | // "postCreateCommand": "go version", 16 | 17 | // Configure tool-specific properties. 18 | // "customizations": {}, 19 | 20 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 21 | // "remoteUser": "root" 22 | } 23 | -------------------------------------------------------------------------------- /src/OrderMonitorService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/OrderMonitorService/Dockerfile: -------------------------------------------------------------------------------- 1 | #build stage 2 | FROM golang:alpine AS builder 3 | RUN apk add --no-cache git 4 | WORKDIR /go/src/app 5 | COPY . . 6 | RUN go get -d -v ./... 7 | RUN go build -o /app/ordermonitor-service ./cmd 8 | 9 | #final stage 10 | FROM alpine:latest 11 | RUN apk --no-cache add ca-certificates 12 | RUN addgroup -S appgroup && adduser -S appuser -G appgroup -h /app 13 | USER appuser 14 | WORKDIR /app 15 | COPY --chown=appuser:appgroup ./config.json . 16 | COPY --chown=appuser:appgroup --from=builder /app/ordermonitor-service . 17 | EXPOSE 5000 18 | ENV PORT=5000 19 | CMD ["./ordermonitor-service"] 20 | -------------------------------------------------------------------------------- /src/OrderMonitorService/cmd/logs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/gin-gonic/gin" 8 | "github.com/sirupsen/logrus" 9 | "github.com/thinktecture-labs/cloud-native-sample/ordermonitorservice/pkg/monitor" 10 | ginlogrus "github.com/toorop/gin-logrus" 11 | ) 12 | 13 | func configureLogging(r *gin.Engine, cfg *monitor.Configuration) *logrus.Logger { 14 | log := logrus.New() 15 | log.SetOutput(ioutil.Discard) 16 | log.SetLevel(logrus.DebugLevel) 17 | 18 | if !cfg.DisableConsoleLog { 19 | log.SetOutput(os.Stdout) 20 | r.Use(ginlogrus.Logger(log)) 21 | } 22 | if cfg.IsProduction() { 23 | log.Infoln("Will run OrderMonitorService in release mode.") 24 | gin.SetMode(gin.ReleaseMode) 25 | } 26 | return log 27 | } 28 | -------------------------------------------------------------------------------- /src/OrderMonitorService/cmd/metrics.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/penglongli/gin-metrics/ginmetrics" 6 | ) 7 | 8 | func configureMetrics(e *gin.Engine) { 9 | m := ginmetrics.GetMonitor() 10 | m.SetMetricPath("/metrics") 11 | m.Use(e) 12 | } 13 | -------------------------------------------------------------------------------- /src/OrderMonitorService/cmd/tracing.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/thinktecture-labs/cloud-native-sample/ordermonitorservice/pkg/monitor" 5 | "go.opentelemetry.io/otel" 6 | "go.opentelemetry.io/otel/exporters/zipkin" 7 | "go.opentelemetry.io/otel/propagation" 8 | "go.opentelemetry.io/otel/sdk/resource" 9 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 10 | semconv "go.opentelemetry.io/otel/semconv/v1.17.0" 11 | ) 12 | 13 | func configureTracing(cfg *monitor.Configuration) (tp *sdktrace.TracerProvider, err error) { 14 | if len(cfg.ZipkinEndpoint) == 0 { 15 | return nil, nil 16 | } 17 | z, err := zipkin.New(cfg.ZipkinEndpoint) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | tp = sdktrace.NewTracerProvider( 23 | sdktrace.WithSampler(sdktrace.AlwaysSample()), 24 | sdktrace.WithBatcher(z), 25 | sdktrace.WithResource(resource.NewWithAttributes( 26 | semconv.SchemaURL, 27 | semconv.ServiceName(serviceName), 28 | )), 29 | ) 30 | 31 | otel.SetTracerProvider(tp) 32 | otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) 33 | return tp, nil 34 | } 35 | -------------------------------------------------------------------------------- /src/OrderMonitorService/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 5000, 3 | "environment": "Development", 4 | "disableConsoleLog": false 5 | } 6 | -------------------------------------------------------------------------------- /src/OrderMonitorService/pkg/controllers/healthz.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | const ( 6 | healthz_prefix = "/healthz" 7 | ) 8 | 9 | func RegisterHealthEndpoints(e *gin.Engine) { 10 | g := e.Group(healthz_prefix) 11 | g.GET("/readiness", func(ctx *gin.Context) { 12 | ctx.JSON(200, gin.H{ 13 | "ready": true, 14 | }) 15 | }) 16 | 17 | g.GET("/liveness", func(ctx *gin.Context) { 18 | ctx.JSON(200, gin.H{ 19 | "alive": true, 20 | }) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /src/OrderMonitorService/pkg/monitor/models.go: -------------------------------------------------------------------------------- 1 | package monitor 2 | 3 | type OrderListModel struct { 4 | Id string `json:"id"` 5 | UserId string `json:"userId"` 6 | Positions []PositionListModel `json:"positions"` 7 | } 8 | 9 | type PositionListModel struct { 10 | ProductId string `json:"productId"` 11 | ProductName string `json:"productName"` 12 | ProductPrice float64 `json:"productPrice"` 13 | ProductDescription string `json:"productDescription"` 14 | Quantity int `json:"quantity"` 15 | } 16 | -------------------------------------------------------------------------------- /src/OrderMonitorService/pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | type ContextKey string 4 | 5 | const ( 6 | AuthKey = ContextKey("Authorization") 7 | BackendTimeOut = ContextKey("BackendTimeOut") 8 | ) 9 | -------------------------------------------------------------------------------- /src/OrdersService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/OrdersService/Configuration/Authorization.cs: -------------------------------------------------------------------------------- 1 | namespace OrdersService.Configuration; 2 | 3 | public class Authorization 4 | { 5 | public string RequiredClaimName { get; set; } = "scope"; 6 | public string RequiredClaimValue {get;set;} = "sample"; 7 | } 8 | -------------------------------------------------------------------------------- /src/OrdersService/Configuration/IdentityServerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace OrdersService.Configuration; 2 | 3 | public class IdentityServerConfiguration 4 | { 5 | public string Authority {get;set;} 6 | public string MetadataAddress {get;set;} 7 | public bool RequireHttpsMetadata { get; set;} = true; 8 | } 9 | -------------------------------------------------------------------------------- /src/OrdersService/CustomMetrics.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Diagnostics.Metrics; 3 | 4 | namespace OrdersService; 5 | 6 | public class CustomMetrics 7 | { 8 | public static readonly Meter Default = new Meter("OrdersService", 9 | Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0"); 10 | 11 | public static readonly Counter OrdersCreated = 12 | Default.CreateCounter("cloud_native_sample_orders_created", 13 | description: "Number of orders created"); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/OrdersService/CustomProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using OpenTelemetry; 3 | 4 | namespace OrdersService; 5 | 6 | internal sealed class CustomProcessor : BaseProcessor 7 | { 8 | public override void OnEnd(Activity activity) 9 | { 10 | if (IsHealthOrMetricsEndpoint(activity.DisplayName)) 11 | { 12 | activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; 13 | } 14 | } 15 | 16 | private static bool IsHealthOrMetricsEndpoint(string displayName) 17 | { 18 | if (string.IsNullOrEmpty(displayName)) 19 | { 20 | return false; 21 | } 22 | return displayName.StartsWith("/healthz/") || 23 | displayName.StartsWith("/metrics"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/OrdersService/Data/Entities/Order.cs: -------------------------------------------------------------------------------- 1 | namespace OrdersService.Data.Entities; 2 | 3 | public class Order 4 | { 5 | public Guid Id { get; set; } 6 | public string UserId { get; set; } 7 | public string? UserName { get; set; } 8 | public List? Positions { get; set; } 9 | public DateTime SubmittedAt { get; set; } 10 | } 11 | 12 | public class OrderPosition 13 | { 14 | public Guid Id { get; set; } 15 | public Guid ProductId { get; set; } 16 | public int Quantity { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/OrdersService/Data/OrdersServiceContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using OrdersService.Data.Entities; 3 | 4 | namespace OrdersService.Data; 5 | 6 | public class OrdersServiceContext : DbContext 7 | { 8 | public OrdersServiceContext(DbContextOptions options) 9 | : base(options) 10 | { 11 | } 12 | 13 | public DbSet Orders { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/OrdersService/Data/Repositories/IOrdersRepository.cs: -------------------------------------------------------------------------------- 1 | using OrdersService.Data.Entities; 2 | 3 | namespace OrdersService.Data.Repositories; 4 | 5 | public interface IOrdersRepository 6 | { 7 | Task AddNewOrderAsync(Order newOrder); 8 | Task> GetAllOrdersAsync(); 9 | } 10 | -------------------------------------------------------------------------------- /src/OrdersService/Data/Repositories/OrdersRepository.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using OrdersService.Data.Entities; 3 | 4 | namespace OrdersService.Data.Repositories; 5 | 6 | public class OrdersRepository : IOrdersRepository 7 | { 8 | private readonly OrdersServiceContext _dbContext; 9 | 10 | public OrdersRepository(OrdersServiceContext dbContext) 11 | { 12 | _dbContext = dbContext; 13 | } 14 | 15 | public async Task AddNewOrderAsync(Order newOrder) 16 | { 17 | _dbContext.Add(newOrder); 18 | await _dbContext.SaveChangesAsync(); 19 | } 20 | 21 | public async Task> GetAllOrdersAsync() 22 | { 23 | return await _dbContext.Orders.Include(o => o.Positions).ToListAsync(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/OrdersService/Extensions/HttpContextExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | 3 | namespace OrdersService.Extensions; 4 | 5 | public static class HttpContextExtensions 6 | { 7 | public static string GetUserName(this HttpContext c) 8 | { 9 | var nameClaim = c.User.FindFirst(ClaimTypes.Name); 10 | return nameClaim != null ? nameClaim.Value : "N/A"; 11 | } 12 | 13 | public static string GetUserId(this HttpContext c) 14 | { 15 | var subClaim = c.User.FindFirst(ClaimTypes.NameIdentifier); 16 | return subClaim != null ? subClaim.Value : "N/A"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/OrdersService/Models/OrderModels.cs: -------------------------------------------------------------------------------- 1 | using Swashbuckle.AspNetCore.Annotations; 2 | 3 | namespace OrdersService.Models 4 | { 5 | [SwaggerSchema()] 6 | public class CreateOrderModel 7 | { 8 | [SwaggerSchema("User's unique identifier")] 9 | public string? UserId { get; set; } 10 | 11 | [SwaggerSchema("List of order positions")] 12 | public List Positions { get; set; } 13 | } 14 | 15 | public class OrderListModel 16 | { 17 | public Guid Id { get; init; } 18 | public string UserId { get; set; } 19 | public List Positions { get; init; } 20 | } 21 | 22 | public class OrderDetailsModel 23 | { 24 | public Guid Id { get; init; } 25 | public string UserId { get; set; } 26 | public string? UserName { get; init; } 27 | public List Positions { get; init; } 28 | public DateTime SubmittedAt { get; init; } 29 | } 30 | 31 | public class OrderPositionModel 32 | { 33 | public Guid ProductId { get; set; } 34 | public int Quantity { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/OrdersService/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:5000", 8 | "sslPort": 44302 9 | } 10 | }, 11 | "profiles": { 12 | "OrdersService": { 13 | "commandName": "Project", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "applicationUrl": "http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "launchUrl": "swagger", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/OrdersService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/OrdersService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information", 6 | "OrdersService.Controllers": "Trace" 7 | } 8 | }, 9 | "OrdersService": { 10 | "CreateOrderPubSubName": "orders", 11 | "CreateOrderTopicName": "new_orders", 12 | "IdentityServer": { 13 | "Authority": "", 14 | "RequireHttpsMetadata": true 15 | }, 16 | "ConnectionString": "" // Set this value to target a MS SQL database, otherwise you'll use the in-memory EF Core provider 17 | }, 18 | "AllowedHosts": "*" 19 | } 20 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/Configuration/Authorization.cs: -------------------------------------------------------------------------------- 1 | namespace PriceDropNotifier.Configuration; 2 | 3 | public class Authorization 4 | { 5 | public string RequiredClaimName { get; set; } = "scope"; 6 | public string RequiredClaimValue {get;set;} = "sample"; 7 | } 8 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/Configuration/IdentityServerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace PriceDropNotifier.Configuration; 2 | 3 | public class IdentityServerConfiguration 4 | { 5 | public string Authority {get;set;} 6 | public string MetadataAddress {get;set;} 7 | public bool RequireHttpsMetadata { get; set;} = true; 8 | } 9 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/Configuration/PriceDropNotifierServiceConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging.Console; 2 | 3 | namespace PriceDropNotifier.Configuration; 4 | 5 | public class PriceDropNotifierServiceConfiguration 6 | { 7 | public PriceDropNotifierServiceConfiguration() 8 | { 9 | IdentityServer = new IdentityServerConfiguration(); 10 | Authorization = new Authorization(); 11 | } 12 | public const string SectionName = "PriceDropNotifierService"; 13 | public string NotificationBindingName = "email"; 14 | public string NotificationBindingOperation = "create"; 15 | public string ConsoleFormatterName { get; set; } = ConsoleFormatterNames.Json; 16 | public bool DisableConsoleLog { get; set; } 17 | public string ZipkinEndpoint { get; set; } 18 | public bool ExposePrometheusMetrics {get;set;} 19 | public string ApplicationInsightsConnectionString {get;set;} 20 | public IdentityServerConfiguration IdentityServer { get;set; } 21 | public Authorization Authorization { get; set; } 22 | } 23 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/CustomProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using OpenTelemetry; 3 | 4 | namespace PriceDropNotifier; 5 | 6 | internal sealed class CustomProcessor : BaseProcessor 7 | { 8 | public override void OnEnd(Activity activity) 9 | { 10 | if (IsHealthOrMetricsEndpoint(activity.DisplayName)) 11 | { 12 | activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; 13 | } 14 | } 15 | 16 | private static bool IsHealthOrMetricsEndpoint(string displayName) 17 | { 18 | if (string.IsNullOrEmpty(displayName)) 19 | { 20 | return false; 21 | } 22 | return displayName.StartsWith("/healthz/") || 23 | displayName.StartsWith("/metrics"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base 2 | WORKDIR /app 3 | EXPOSE 5000 4 | 5 | ENV ASPNETCORE_URLS=http://+:5000 6 | 7 | # Creates a non-root user with an explicit UID and adds permission to access the /app folder 8 | # For more info, please refer to https://aka.ms/vscode-docker-dotnet-configure-containers 9 | RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app 10 | USER appuser 11 | 12 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 13 | WORKDIR /src 14 | COPY ["PriceDropNotifier.csproj", "./"] 15 | RUN dotnet restore "PriceDropNotifier.csproj" 16 | COPY . . 17 | WORKDIR "/src/." 18 | RUN dotnet build "PriceDropNotifier.csproj" -c Release -o /app/build 19 | 20 | FROM build AS publish 21 | RUN dotnet publish "PriceDropNotifier.csproj" -c Release -o /app/publish /p:UseAppHost=false 22 | 23 | FROM base AS final 24 | WORKDIR /app 25 | COPY --from=publish /app/publish . 26 | ENTRYPOINT ["dotnet", "PriceDropNotifier.dll"] 27 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/Extensions/HttpRequestExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNetCore.Http; 2 | 3 | public static class HttpRequestExtensions 4 | { 5 | 6 | const string DaprApiTokenHeaderName = "dapr-api-token"; 7 | const string DaprApiTokenEnvironmentVariableName = "DAPR_API_TOKEN"; 8 | public static bool HasValidDaprApiToken(this HttpRequest request) 9 | { 10 | if (request.Headers.TryGetValue(DaprApiTokenHeaderName, out var tokens)) 11 | { 12 | var expected = GetDaprApiToken(); 13 | var actual = tokens.FirstOrDefault(); 14 | return expected == actual; 15 | } 16 | return false; 17 | } 18 | 19 | private static string GetDaprApiToken() 20 | { 21 | var token = Environment.GetEnvironmentVariable(DaprApiTokenEnvironmentVariableName); 22 | if (string.IsNullOrEmpty(token)) 23 | { 24 | throw new InvalidOperationException($"{DaprApiTokenEnvironmentVariableName} environment variable is not set"); 25 | } 26 | return token; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/Models/NotificationRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace PriceDropNotifier.Models; 4 | 5 | public class NotificationRequest 6 | { 7 | 8 | [JsonPropertyName("recipient")] 9 | public string Recipient {get;set;} 10 | [JsonPropertyName("productName")] 11 | public string ProductName {get;set;} 12 | [JsonPropertyName("price")] 13 | public double Price {get;set;} 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:34505", 8 | "sslPort": 44346 9 | } 10 | }, 11 | "profiles": { 12 | "PriceDropNotifier": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:7273;http://localhost:5104", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/PriceDropNotifierService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "PriceDropNotifierService": { 9 | "DisableConsoleLog": false, 10 | "ExposePrometheusMetrics": true, 11 | "IdentityServer": { 12 | "Authority": "", 13 | "RequireHttpsMetadata": true 14 | } 15 | }, 16 | "AllowedHosts": "*" 17 | } 18 | -------------------------------------------------------------------------------- /src/PriceWatcherService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Configuration/Authorization.cs: -------------------------------------------------------------------------------- 1 | namespace PriceWatcher.Configuration; 2 | 3 | public class Authorization 4 | { 5 | public string RequiredClaimName { get; set; } = "scope"; 6 | public string RequiredClaimValue {get;set;} = "sample"; 7 | public string AdminScopeName {get;set;} = "admin"; 8 | } 9 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Configuration/IdentityServerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace PriceWatcher.Configuration; 2 | 3 | public class IdentityServerConfiguration 4 | { 5 | public string Authority {get;set;} 6 | public string MetadataAddress {get;set;} 7 | public bool RequireHttpsMetadata { get; set;} = true; 8 | } 9 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Configuration/PriceWatcherServiceConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging.Console; 2 | 3 | namespace PriceWatcher.Configuration; 4 | 5 | public class PriceWatcherServiceConfiguration 6 | { 7 | public PriceWatcherServiceConfiguration() 8 | { 9 | IdentityServer = new IdentityServerConfiguration(); 10 | Authorization = new Authorization(); 11 | } 12 | public const string SectionName = "PriceWatcherService"; 13 | public string PriceDropsPubSubName { get; set; } = "pricedrops"; 14 | public string PriceDropsTopicName { get; set; } = "notifications"; 15 | public string ConsoleFormatterName { get; set; } = ConsoleFormatterNames.Json; 16 | public bool DisableConsoleLog { get; set; } 17 | public string ZipkinEndpoint { get; set; } 18 | public bool ExposePrometheusMetrics {get;set;} 19 | public string ApplicationInsightsConnectionString {get;set;} 20 | public IdentityServerConfiguration IdentityServer { get;set; } 21 | public Authorization Authorization { get; set; } 22 | } 23 | -------------------------------------------------------------------------------- /src/PriceWatcherService/CustomMetrics.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Diagnostics.Metrics; 3 | 4 | namespace PriceWatcher; 5 | 6 | public class CustomMetrics 7 | { 8 | public static readonly Meter Default = new Meter("PriceWatcherService", 9 | Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0"); 10 | 11 | public static readonly Counter PriceWatchers = 12 | Default.CreateCounter("cloud_native_new_price_watch", 13 | description: "Number of users watching for a price drop"); 14 | 15 | public static readonly Counter PriceDrops = 16 | Default.CreateCounter("cloud_native_price_drop", 17 | description: "Number of a price drop issued"); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/PriceWatcherService/CustomProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using OpenTelemetry; 3 | 4 | namespace PriceWatcher; 5 | 6 | internal sealed class CustomProcessor : BaseProcessor 7 | { 8 | public override void OnEnd(Activity activity) 9 | { 10 | if (IsHealthOrMetricsEndpoint(activity.DisplayName)) 11 | { 12 | activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; 13 | } 14 | } 15 | 16 | private static bool IsHealthOrMetricsEndpoint(string displayName) 17 | { 18 | if (string.IsNullOrEmpty(displayName)) 19 | { 20 | return false; 21 | } 22 | return displayName.StartsWith("/healthz/") || 23 | displayName.StartsWith("/metrics"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base 2 | WORKDIR /app 3 | EXPOSE 5000 4 | 5 | ENV ASPNETCORE_URLS=http://+:5000 6 | 7 | # Creates a non-root user with an explicit UID and adds permission to access the /app folder 8 | # For more info, please refer to https://aka.ms/vscode-docker-dotnet-configure-containers 9 | RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app 10 | USER appuser 11 | 12 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 13 | WORKDIR /src 14 | COPY ["PriceWatcher.csproj", "./"] 15 | RUN dotnet restore "PriceWatcher.csproj" 16 | COPY . . 17 | WORKDIR "/src/." 18 | RUN dotnet build "PriceWatcher.csproj" -c Release -o /app/build 19 | 20 | FROM build AS publish 21 | RUN dotnet publish "PriceWatcher.csproj" -c Release -o /app/publish /p:UseAppHost=false 22 | 23 | FROM base AS final 24 | WORKDIR /app 25 | COPY --from=publish /app/publish . 26 | ENTRYPOINT ["dotnet", "PriceWatcher.dll"] 27 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Entities/Products.cs: -------------------------------------------------------------------------------- 1 | namespace PriceWatcher.Entities; 2 | 3 | public class Product 4 | { 5 | 6 | public Guid Id { get; set; } 7 | public string Name { get; set; } 8 | public string Description {get;set;} 9 | public double Price { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Entities/Watcher.cs: -------------------------------------------------------------------------------- 1 | namespace PriceWatcher.Entities; 2 | 3 | public class Watcher 4 | { 5 | public string Email { get; set; } 6 | public Guid ProductId { get; set; } 7 | public double Price { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Models/PriceDropModel.cs: -------------------------------------------------------------------------------- 1 | namespace PriceWatcher.Models; 2 | 3 | public class PriceDropModel 4 | { 5 | public Guid ProductId { get; set; } 6 | public double DropPriceBy { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Models/PriceDropNotificationModel.cs: -------------------------------------------------------------------------------- 1 | namespace PriceWatcher.Models 2 | { 3 | public class PriceDropNotificationModel 4 | { 5 | public string Recipient { get; set; } 6 | public string ProductName { get; set; } 7 | public double Price { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Models/RegisterModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace PriceWatcher.Models; 4 | 5 | public class RegisterModel 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | 11 | [Required] 12 | public Guid ProductId { get; set; } 13 | 14 | [Required] 15 | public double Price { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Models/ResponseMessage.cs: -------------------------------------------------------------------------------- 1 | namespace PriceWatcher.Models; 2 | 3 | public class ResponseMessage 4 | { 5 | public string Message { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:15562", 8 | "sslPort": 44326 9 | } 10 | }, 11 | "profiles": { 12 | "PriceWatcher": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:7045;http://localhost:5281", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/PriceWatcherService/Repositories/IPriceWatcherRepository.cs: -------------------------------------------------------------------------------- 1 | namespace PriceWatcher.Repositories; 2 | 3 | public interface IPriceWatcherRepository 4 | { 5 | bool Register(string email, Guid productId, double price); 6 | bool DropPrice(Guid productId, double dropBy); 7 | } 8 | -------------------------------------------------------------------------------- /src/PriceWatcherService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/PriceWatcherService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information" 6 | } 7 | }, 8 | "PriceWatcherService": { 9 | "DisableConsoleLog": false, 10 | "ExposePrometheusMetrics": true, 11 | "IdentityServer": { 12 | "Authority": "", 13 | "RequireHttpsMetadata": true 14 | } 15 | }, 16 | "AllowedHosts": "*" 17 | } 18 | -------------------------------------------------------------------------------- /src/ProductsService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/ProductsService/Configuration/Authorization.cs: -------------------------------------------------------------------------------- 1 | namespace ProductsService.Configuration; 2 | 3 | public class Authorization 4 | { 5 | public string RequiredClaimName { get; set; } = "scope"; 6 | public string RequiredClaimValue {get;set;} = "sample"; 7 | } 8 | -------------------------------------------------------------------------------- /src/ProductsService/Configuration/IdentityServerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace ProductsService.Configuration; 2 | 3 | public class IdentityServerConfiguration 4 | { 5 | public string Authority {get;set;} 6 | public string MetadataAddress {get;set;} 7 | public bool RequireHttpsMetadata { get; set;} = true; 8 | } 9 | -------------------------------------------------------------------------------- /src/ProductsService/Configuration/ProductsServiceConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging.Console; 2 | 3 | namespace ProductsService.Configuration; 4 | 5 | public class ProductsServiceConfiguration 6 | { 7 | public const string SectionName = "ProductsService"; 8 | 9 | public ProductsServiceConfiguration() 10 | { 11 | IdentityServer = new IdentityServerConfiguration(); 12 | Authorization = new Authorization(); 13 | } 14 | public string ConsoleFormatterName { get; set; } = ConsoleFormatterNames.Json; 15 | public bool DisableConsoleLog { get; set; } 16 | public string ZipkinEndpoint { get; set; } 17 | public bool ExposePrometheusMetrics {get;set;} 18 | public string ApplicationInsightsConnectionString {get;set;} 19 | 20 | public IdentityServerConfiguration IdentityServer { get;set; } 21 | public Authorization Authorization { get; set; } 22 | public string ConnectionString { get; set; } 23 | } 24 | -------------------------------------------------------------------------------- /src/ProductsService/CustomProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using OpenTelemetry; 3 | 4 | namespace ProductsService; 5 | 6 | internal sealed class CustomProcessor : BaseProcessor 7 | { 8 | public override void OnEnd(Activity activity) 9 | { 10 | if (IsHealthOrMetricsEndpoint(activity.DisplayName)) 11 | { 12 | activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; 13 | } 14 | } 15 | 16 | private static bool IsHealthOrMetricsEndpoint(string displayName) 17 | { 18 | if (string.IsNullOrEmpty(displayName)) 19 | { 20 | return false; 21 | } 22 | return displayName.StartsWith("/healthz/") || 23 | displayName.StartsWith("/metrics"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ProductsService/Data/Entities/Product.cs: -------------------------------------------------------------------------------- 1 | namespace ProductsService.Data.Entities 2 | { 3 | public class Product 4 | { 5 | public Product(Guid id, string name, string description, IEnumerable categories, double price) 6 | { 7 | Id = id; 8 | Name = name; 9 | Description = description; 10 | Categories = categories; 11 | Price = price; 12 | } 13 | 14 | public Guid Id { get; set; } 15 | public string Name { get; set; } 16 | public string Description { get; set; } 17 | public IEnumerable Categories { get; set; } 18 | public double Price { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ProductsService/Data/Repositories/IProductsRepository.cs: -------------------------------------------------------------------------------- 1 | using ProductsService.Data.Entities; 2 | 3 | namespace ProductsService.Data.Repositories 4 | { 5 | public interface IProductsRepository 6 | { 7 | Task CreateAsync(Product product); 8 | Task> GetAllAsync(); 9 | Task GetByIdAsync(Guid id); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ProductsService/Extensions/ProductExtensions.cs: -------------------------------------------------------------------------------- 1 | using ProductsService.Data.Entities; 2 | using ProductsService.Models; 3 | 4 | namespace ProductsService.Extensions; 5 | 6 | public static class ProductExtensions 7 | { 8 | public static Product ToEntity(this ProductCreateModel p) 9 | { 10 | return new Product(Guid.Empty, p.Name, p.Description, p.Tags, p.Price); 11 | 12 | } 13 | public static ProductListModel ToListModel(this Product p) 14 | { 15 | return new ProductListModel 16 | { 17 | Id = p.Id, 18 | Name = p.Name, 19 | Description = p.Description, 20 | Price = p.Price 21 | }; 22 | } 23 | 24 | public static ProductDetailsModel ToDetailsModel(this Product p) 25 | { 26 | return new ProductDetailsModel 27 | { 28 | Id = p.Id, 29 | Name = p.Name, 30 | Description = p.Description, 31 | Price = p.Price, 32 | Tags = p.Categories.ToArray() 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/ProductsService/Migrations/IMigration.cs: -------------------------------------------------------------------------------- 1 | using System.Data.SqlClient; 2 | 3 | namespace ProductsService.Migrations; 4 | 5 | public interface IMigration 6 | { 7 | int Version { get; } 8 | string Script {get;} 9 | void PostMigrate(SqlConnection con); 10 | } 11 | -------------------------------------------------------------------------------- /src/ProductsService/Models/ProductModels.cs: -------------------------------------------------------------------------------- 1 | namespace ProductsService.Models; 2 | 3 | public class ProductCreateModel { 4 | public string Name { get; set; } 5 | public string Description { get; set; } 6 | public string[] Tags {get;set;} 7 | public double Price { get; set; } 8 | } 9 | 10 | public class ProductListModel 11 | { 12 | public Guid Id { get; set; } 13 | public string Name { get; set; } 14 | public string Description { get; set; } 15 | public double Price { get; set; } 16 | } 17 | 18 | public class ProductDetailsModel 19 | { 20 | public Guid Id { get; set; } 21 | public string Name { get; set; } 22 | public string Description { get; set; } 23 | public string[] Tags { get; set; } 24 | public double Price { get; set; } 25 | } 26 | -------------------------------------------------------------------------------- /src/ProductsService/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:5000", 8 | "sslPort": 44301 9 | } 10 | }, 11 | "profiles": { 12 | "ProductsService": { 13 | "commandName": "Project", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "applicationUrl": "http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "launchUrl": "swagger", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/ProductsService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/ProductsService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /src/ShippingService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/bin 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /src/ShippingService/.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | -------------------------------------------------------------------------------- /src/ShippingService/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "sdktrace" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/ShippingService/Dockerfile: -------------------------------------------------------------------------------- 1 | #build stage 2 | FROM golang:alpine AS builder 3 | RUN apk add --no-cache git 4 | WORKDIR /go/src/app 5 | COPY . . 6 | RUN go get -d -v ./... 7 | RUN go build -o /app/shipping-service ./cmd 8 | 9 | #final stage 10 | FROM alpine:latest 11 | RUN apk --no-cache add ca-certificates 12 | RUN addgroup -S appgroup && adduser -S appuser -G appgroup -h /app 13 | USER appuser 14 | WORKDIR /app 15 | COPY --chown=appuser:appgroup ./config.json . 16 | COPY --chown=appuser:appgroup --from=builder /app/shipping-service . 17 | EXPOSE 5000 18 | ENV PORT=5000 19 | CMD ["./shipping-service"] 20 | -------------------------------------------------------------------------------- /src/ShippingService/cmd/logs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/gin-gonic/gin" 8 | "github.com/sirupsen/logrus" 9 | "github.com/thinktecture-labs/cloud-native-sample/shipping-service/pkg/shipping" 10 | ginlogrus "github.com/toorop/gin-logrus" 11 | ) 12 | 13 | func configureLogging(r *gin.Engine, cfg *shipping.Configuration) *logrus.Logger { 14 | log := logrus.New() 15 | log.SetOutput(ioutil.Discard) 16 | log.SetLevel(logrus.DebugLevel) 17 | 18 | if !cfg.DisableConsoleLog { 19 | log.SetOutput(os.Stdout) 20 | r.Use(ginlogrus.Logger(log)) 21 | } 22 | if cfg.IsProduction() { 23 | log.Infoln("Will run shipping service in release mode.") 24 | gin.SetMode(gin.ReleaseMode) 25 | } 26 | return log 27 | } 28 | -------------------------------------------------------------------------------- /src/ShippingService/cmd/metrics.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/penglongli/gin-metrics/ginmetrics" 6 | ) 7 | 8 | func configureMetrics(e *gin.Engine) { 9 | m := ginmetrics.GetMonitor() 10 | m.SetMetricPath("/metrics") 11 | m.Use(e) 12 | } 13 | -------------------------------------------------------------------------------- /src/ShippingService/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 5003, 3 | "environment": "Development", 4 | "sourcePubSubName": "orders", 5 | "sourceTopicName": "new_orders", 6 | "targetPubSubName": "orders", 7 | "targetTopicName": "processed_orders", 8 | "disableConsoleLog": false 9 | } 10 | -------------------------------------------------------------------------------- /src/ShippingService/pkg/cloudevents/schema.go: -------------------------------------------------------------------------------- 1 | package cloudevents 2 | 3 | import "github.com/thinktecture-labs/cloud-native-sample/shipping-service/pkg/shipping" 4 | 5 | type CloudEvent struct { 6 | Id string `json:"id"` 7 | Type string `json:"type"` 8 | Data shipping.Order `json:"data"` 9 | } 10 | -------------------------------------------------------------------------------- /src/cn-sample/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | 3 | build: 4 | @echo "Building..." 5 | @mage -goos windows -goarch amd64 -compile ../../tools/win_amd64/cn-sample.exe 6 | @mage -goos windows -goarch arm64 -compile ../../tools/win_arm64/cn-sample.exe 7 | @mage -goos darwin -goarch amd64 -compile ../../tools/darwin_amd64/cn-sample 8 | @mage -goos darwin -goarch arm64 -compile ../../tools/darwin_arm64/cn-sample 9 | @mage -goos linux -goarch amd64 -compile ../../tools/linux_amd64/cn-sample 10 | -------------------------------------------------------------------------------- /src/cn-sample/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thinktecture-labs/cloud-native-sample/cn-sample 2 | 3 | go 1.20 4 | 5 | require github.com/magefile/mage v1.14.0 6 | -------------------------------------------------------------------------------- /src/cn-sample/go.sum: -------------------------------------------------------------------------------- 1 | github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= 2 | github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= 3 | -------------------------------------------------------------------------------- /tools/darwin_amd64/cn-sample: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/tools/darwin_amd64/cn-sample -------------------------------------------------------------------------------- /tools/darwin_arm64/cn-sample: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/tools/darwin_arm64/cn-sample -------------------------------------------------------------------------------- /tools/linux_amd64/cn-sample: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/tools/linux_amd64/cn-sample -------------------------------------------------------------------------------- /tools/win_amd64/cn-sample.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/tools/win_amd64/cn-sample.exe -------------------------------------------------------------------------------- /tools/win_arm64/cn-sample.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture-labs/cloud-native-sample/31840135ec3b88a226835b6616f636006d340949/tools/win_arm64/cn-sample.exe --------------------------------------------------------------------------------