From 5405b39e1edd29da3c16844e42a74db3f407078f Mon Sep 17 00:00:00 2001 From: Richard87 Date: Mon, 3 Jun 2024 16:30:42 +0200 Subject: [PATCH 01/11] Update and refactor Dependency injection and configuration --- .../Controllers/OrdersController.cs | 53 +++---- .../Controllers/QueueController.cs | 24 +-- src/Keda.Samples.DotNet.Web/Dockerfile | 4 +- ...ons.cs => AddSwaggerServicesExtensions.cs} | 6 +- .../Keda.Samples.DotNet.Web.Open-Api.xml | 8 +- .../Keda.Samples.DotNet.Web.csproj | 23 ++- .../OrderQueueSettings.cs | 7 - src/Keda.Samples.DotNet.Web/Program.cs | 49 +++--- src/Keda.Samples.DotNet.Web/Startup.cs | 77 --------- src/Keda.Samples.DotNet.Web/appsettings.json | 11 +- .../AddServiceBusClientExtension.cs | 85 ++++++++++ .../Keda.Samples.Dotnet.Contracts.csproj | 24 ++- .../QueueStatus.cs | 12 +- .../Keda.Samples.Dotnet.OrderGenerator.csproj | 13 +- .../Program.cs | 94 ++++++----- .../appsettings.json | 18 +++ .../Dockerfile | 4 +- .../Keda.Samples.Dotnet.OrderProcessor.csproj | 11 +- .../OrdersQueueProcessor.cs | 14 +- .../Program.cs | 32 +--- .../QueueWorker.cs | 149 +++++++----------- .../ServiceBusClientFactory.cs | 46 ------ .../appsettings.json | 9 ++ src/global.json | 6 + 24 files changed, 344 insertions(+), 435 deletions(-) rename src/Keda.Samples.DotNet.Web/Extensions/{IServiceCollectionExtensions.cs => AddSwaggerServicesExtensions.cs} (79%) delete mode 100644 src/Keda.Samples.DotNet.Web/OrderQueueSettings.cs delete mode 100644 src/Keda.Samples.DotNet.Web/Startup.cs create mode 100644 src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs create mode 100644 src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json delete mode 100644 src/Keda.Samples.Dotnet.OrderProcessor/ServiceBusClientFactory.cs create mode 100644 src/global.json diff --git a/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs b/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs index f480db1..7eb1d2c 100644 --- a/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs +++ b/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs @@ -1,47 +1,34 @@ using System.ComponentModel.DataAnnotations; -using System.Text; +using System.Text.Json; +using System.Threading; using System.Threading.Tasks; -using GuardNet; +using Azure.Messaging.ServiceBus; using Keda.Samples.Dotnet.Contracts; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.ServiceBus; -using Newtonsoft.Json; -namespace Keda.Samples.DotNet.Web.Controllers +namespace Keda.Samples.DotNet.Web.Controllers; + +/// +/// API endpoint to manage orders +/// +[ApiController] +[Route("api/v1/orders")] +public class OrdersController : ControllerBase { + /// - /// API endpoint to manage orders + /// Create Order /// - [ApiController] - [Route("api/v1/orders")] - public class OrdersController : ControllerBase + [HttpPost(Name = "Order_Create")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task Create([FromBody, Required] Order order, [FromServices] ServiceBusSender sender, CancellationToken ct) { - private readonly QueueClient _queueClient; - - /// - /// Constructor - /// - /// Client to send messages to queue with - public OrdersController(QueueClient queueClient) - { - Guard.NotNull(queueClient, nameof(queueClient)); - - _queueClient = queueClient; - } + var jsonString = JsonSerializer.Serialize(order); + var orderMessage = new ServiceBusMessage(jsonString); - /// - /// Create Order - /// - [HttpPost(Name = "Order_Create")] - [ProducesResponseType(StatusCodes.Status201Created)] - public async Task Create([FromBody, Required] Order order) - { - var rawOrder = JsonConvert.SerializeObject(order); - var orderMessage = new Message(Encoding.UTF8.GetBytes(rawOrder)); - await _queueClient.SendAsync(orderMessage); + await sender.SendMessageAsync(orderMessage, ct); - return Accepted(); - } + return Accepted(); } } \ No newline at end of file diff --git a/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs b/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs index f1907b0..adfd661 100644 --- a/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs +++ b/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs @@ -1,9 +1,6 @@ -using System.Threading.Tasks; +using Azure.Messaging.ServiceBus; using Keda.Samples.Dotnet.Contracts; using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Management; -using Microsoft.Extensions.Configuration; namespace Keda.Samples.DotNet.Web.Controllers { @@ -11,27 +8,14 @@ namespace Keda.Samples.DotNet.Web.Controllers [Route("api/v1/[controller]")] public class QueueController : ControllerBase { - protected IConfiguration Configuration { get; } - - public QueueController(IConfiguration configuration) - { - Configuration = configuration; - } - [HttpGet] [ApiExplorerSettings(IgnoreApi = true)] - public async Task Get() + public QueueStatus Get([FromServices] ServiceBusReceiver receiver) { - var connectionString = Configuration.GetValue("KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING"); - - // Check current queue length - var client = new ManagementClient(new ServiceBusConnectionStringBuilder(connectionString)); - var queueInfo = await client.GetQueueRuntimeInfoAsync("orders"); - return new QueueStatus { - MessageCount = queueInfo.MessageCount + MessageCount = receiver.PrefetchCount }; } } -} \ No newline at end of file +} diff --git a/src/Keda.Samples.DotNet.Web/Dockerfile b/src/Keda.Samples.DotNet.Web/Dockerfile index b155571..d6e4a4a 100644 --- a/src/Keda.Samples.DotNet.Web/Dockerfile +++ b/src/Keda.Samples.DotNet.Web/Dockerfile @@ -1,9 +1,9 @@ -FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.1-alpine AS base +FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/core/sdk:3.1.101-alpine AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj", "Keda.Samples.DotNet.Web/"] COPY ["Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj", "Keda.Samples.Dotnet.Contracts/"] diff --git a/src/Keda.Samples.DotNet.Web/Extensions/IServiceCollectionExtensions.cs b/src/Keda.Samples.DotNet.Web/Extensions/AddSwaggerServicesExtensions.cs similarity index 79% rename from src/Keda.Samples.DotNet.Web/Extensions/IServiceCollectionExtensions.cs rename to src/Keda.Samples.DotNet.Web/Extensions/AddSwaggerServicesExtensions.cs index eabd0d3..0fa919a 100644 --- a/src/Keda.Samples.DotNet.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/Keda.Samples.DotNet.Web/Extensions/AddSwaggerServicesExtensions.cs @@ -4,9 +4,9 @@ namespace Microsoft.Extensions.DependencyInjection { - public static class IServiceCollectionExtensions + public static class AddSwaggerServicesExtensions { - public static IServiceCollection AddSwagger(this IServiceCollection services) + public static void AddSwagger(this IServiceCollection services) { var openApiInformation = new OpenApiInfo { @@ -20,8 +20,6 @@ public static IServiceCollection AddSwagger(this IServiceCollection services) swaggerGenerationOptions.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Keda.Samples.DotNet.Web.Open-Api.xml")); }); - - return services; } } } \ No newline at end of file diff --git a/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.Open-Api.xml b/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.Open-Api.xml index f970cc4..9bb585c 100644 --- a/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.Open-Api.xml +++ b/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.Open-Api.xml @@ -9,13 +9,7 @@ API endpoint to manage orders - - - Constructor - - Client to send messages to queue with - - + Create Order diff --git a/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj b/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj index 2b825b9..8fef638 100644 --- a/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj +++ b/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj @@ -1,7 +1,8 @@  - netcoreapp3.1 + net8.0 + 12 ..\docker-compose.dcproj 858adc6c-c378-4698-a3c4-1112a29d6561 true @@ -10,19 +11,13 @@ - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + + + + + diff --git a/src/Keda.Samples.DotNet.Web/OrderQueueSettings.cs b/src/Keda.Samples.DotNet.Web/OrderQueueSettings.cs deleted file mode 100644 index 9712444..0000000 --- a/src/Keda.Samples.DotNet.Web/OrderQueueSettings.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Keda.Samples.DotNet.Web -{ - public class OrderQueueSettings - { - public string ConnectionString { get; set; } - } -} diff --git a/src/Keda.Samples.DotNet.Web/Program.cs b/src/Keda.Samples.DotNet.Web/Program.cs index dde44e4..602a13e 100644 --- a/src/Keda.Samples.DotNet.Web/Program.cs +++ b/src/Keda.Samples.DotNet.Web/Program.cs @@ -1,25 +1,34 @@ -using Microsoft.AspNetCore.Hosting; +using Keda.Samples.Dotnet.Contracts; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace Keda.Samples.DotNet.Web +var builder = WebApplication.CreateBuilder(); + +builder.Services.AddLogging(builder => builder.AddConsole()); +builder.Services.AddControllers(); +builder.Services.AddRazorPages(); +builder.Services.AddSignalR(); +builder.Services.AddSwagger(); +builder.Services.AddOrderQueueServices(); + + +var app = builder.Build(); +if (app.Environment.IsDevelopment())app.UseDeveloperExceptionPage(); +else app.UseExceptionHandler("/Error"); + +app.UseStaticFiles(); +app.UseRouting(); +app.UseAuthorization(); +app.MapRazorPages(); +app.MapControllers(); + +app.UseSwagger(); +app.UseSwaggerUI(swaggerUiOptions => { - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } + swaggerUiOptions.SwaggerEndpoint("v1/swagger.json", "Keda.Samples.Dotnet.API"); + swaggerUiOptions.DocumentTitle = "KEDA API"; +}); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.ConfigureLogging((hostBuilderContext, loggingBuilder) => - { - loggingBuilder.AddConsole(consoleLoggerOptions => consoleLoggerOptions.TimestampFormat = "[HH:mm:ss]"); - }); - webBuilder.UseStartup(); - }); - } -} +await app.RunAsync(); diff --git a/src/Keda.Samples.DotNet.Web/Startup.cs b/src/Keda.Samples.DotNet.Web/Startup.cs deleted file mode 100644 index 381a80e..0000000 --- a/src/Keda.Samples.DotNet.Web/Startup.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.IO; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Azure.ServiceBus; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.OpenApi.Models; - -namespace Keda.Samples.DotNet.Web -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers(); - services.AddRazorPages(); - services.AddSignalR(); - - services.AddOptions(); - var orderQueueSection = Configuration.GetSection("OrderQueue"); - services.Configure(orderQueueSection); - - services.AddSwagger(); - services.AddScoped(serviceProvider => - { - var connectionString = Configuration.GetValue("KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING"); - var serviceBusConnectionStringBuilder = new ServiceBusConnectionStringBuilder(connectionString); - return new QueueClient(serviceBusConnectionStringBuilder.GetNamespaceConnectionString(), serviceBusConnectionStringBuilder.EntityPath); - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } - - app.UseHttpsRedirection(); - app.UseStaticFiles(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapRazorPages(); - endpoints.MapControllers(); - }); - - app.UseSwagger(); - app.UseSwaggerUI(swaggerUiOptions => - { - swaggerUiOptions.SwaggerEndpoint("v1/swagger.json", "Keda.Samples.Dotnet.API"); - swaggerUiOptions.DocumentTitle = "KEDA API"; - }); - } - } -} diff --git a/src/Keda.Samples.DotNet.Web/appsettings.json b/src/Keda.Samples.DotNet.Web/appsettings.json index 8ed479d..3cf13cd 100644 --- a/src/Keda.Samples.DotNet.Web/appsettings.json +++ b/src/Keda.Samples.DotNet.Web/appsettings.json @@ -7,8 +7,13 @@ } }, "AllowedHosts": "*", - "OrderQueue": { - "ConnectionString": "KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING" + "OrderQueueOptions": { + "QueueName": "main", + "AuthMode": "WorkloadIdentity", + "ConnectionString": "", + "FullyQualifiedNamespace": "", + "TenantId": "", + "ClientId": "b96d264b-7053-4465-a4a7-32be5b0fec49", + "ClientSecret": "", } - } diff --git a/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs b/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs new file mode 100644 index 0000000..666f172 --- /dev/null +++ b/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs @@ -0,0 +1,85 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Azure.Identity; +using Azure.Messaging.ServiceBus; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Logging; + +namespace Keda.Samples.Dotnet.Contracts; + +public static class AddServiceBusClientExtension +{ + public static IServiceCollection AddOrderQueueServices(this IServiceCollection services) + { + services.AddSingleton(svc => + { + var logger = svc.GetRequiredService>(); + var options = svc.GetRequiredService>(); + return AuthenticateToAzureServiceBus(options.Value, logger); + }); + services.AddScoped(svc => + { + var client = svc.GetRequiredService(); + var options = svc.GetRequiredService>(); + return client.CreateReceiver(options.Value.QueueName); + }); + services.AddScoped(svc => + { + var client = svc.GetRequiredService(); + var options = svc.GetRequiredService>(); + return client.CreateSender(options.Value.QueueName); + }); + services.AddScoped(svc => + { + var client = svc.GetRequiredService(); + var options = svc.GetRequiredService>(); + return client.CreateProcessor(options.Value.QueueName); + }); + + return services; + } + + private static ServiceBusClient AuthenticateToAzureServiceBus(OrderQueueOptions options, ILogger logger) + { + switch (options.AuthMode) + { + case AuthenticationMode.ConnectionString: + logger.LogInformation($"Authentication by using connection string"); + return new ServiceBusClient(options.ConnectionString); + case AuthenticationMode.ServicePrinciple: + logger.LogInformation("Authentication by using service principle {ClientId}", options.ClientId); + return new ServiceBusClient(options.FullyQualifiedNamespace, new ClientSecretCredential(options.TenantId, options.ClientId, options.ClientSecret)); + case AuthenticationMode.PodIdentity: + logger.LogInformation("Authentication by using pod identity {ClientId}", options.ClientId); + return new ServiceBusClient(options.FullyQualifiedNamespace, new ManagedIdentityCredential(options.ClientId)); + case AuthenticationMode.WorkloadIdentity: + logger.LogInformation("Authentication by using workload identity {ClientId}", options.ClientId); + return new ServiceBusClient(options.FullyQualifiedNamespace, new ManagedIdentityCredential(options.ClientId)); + default: + throw new ArgumentOutOfRangeException("AuthMode","AuthMode not supported"); + } + } +} + +public enum AuthenticationMode +{ + ConnectionString, + ServicePrinciple, + PodIdentity, + WorkloadIdentity +} +public class OrderQueueOptions +{ + [Required] + public AuthenticationMode AuthMode { get; set; } + [Required] + public string QueueName { get; set; } + + public string ConnectionString { get; set; } + public string FullyQualifiedNamespace { get; set;} + public string TenantId { get; set;} + public string ClientId { get; set;} + public string ClientSecret { get; set;} +} diff --git a/src/Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj b/src/Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj index a20524d..a8e71ef 100644 --- a/src/Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj +++ b/src/Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj @@ -1,11 +1,31 @@ - netcoreapp3.1 + net8.0 + 12 - + + + + + + + + + + + ..\..\..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\8.0.6\Microsoft.Extensions.Configuration.dll + + + + ..\..\..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\8.0.6\Microsoft.Extensions.Hosting.dll + + + ..\..\..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\8.0.6\Microsoft.Extensions.Hosting.Abstractions.dll + + diff --git a/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs b/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs index 378644b..4f4def3 100644 --- a/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs +++ b/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs @@ -1,10 +1,6 @@ -using Newtonsoft.Json; +namespace Keda.Samples.Dotnet.Contracts; -namespace Keda.Samples.Dotnet.Contracts +public class QueueStatus { - public class QueueStatus - { - [JsonProperty] - public long MessageCount { get; set; } - } -} + public long MessageCount { get; set; } +} \ No newline at end of file diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/Keda.Samples.Dotnet.OrderGenerator.csproj b/src/Keda.Samples.Dotnet.OrderGenerator/Keda.Samples.Dotnet.OrderGenerator.csproj index d8ba517..afe2f68 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/Keda.Samples.Dotnet.OrderGenerator.csproj +++ b/src/Keda.Samples.Dotnet.OrderGenerator/Keda.Samples.Dotnet.OrderGenerator.csproj @@ -1,18 +1,21 @@  + net8.0 + 12 Exe - netcoreapp3.1 - - - + - + + + + + diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs index e21fdca..d803349 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs @@ -1,68 +1,62 @@ using Bogus; using Keda.Samples.Dotnet.Contracts; -using Newtonsoft.Json; using System; +using System.Text.Json; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; -namespace Keda.Samples.Dotnet.OrderGenerator -{ - class Program - { - private const string QueueName = ""; - private const string ConnectionString = ""; - - static async Task Main(string[] args) - { - Console.WriteLine("Let's queue some orders, how many do you want?"); +var builder = Host.CreateApplicationBuilder(); +builder.Services.Configure(builder.Configuration.GetSection(key: nameof(OrderQueueOptions))); +builder.Services.AddOrderQueueServices(); +var app = builder.Build(); +var sender = app.Services.GetRequiredService(); - var requestedAmount = DetermineOrderAmount(); - await QueueOrders(requestedAmount); +Console.WriteLine("Let's queue some orders, how many do you want?"); - Console.WriteLine("That's it, see you later!"); - } +var requestedAmount = DetermineOrderAmount(); +await QueueOrders(requestedAmount, sender); - private static async Task QueueOrders(int requestedAmount) - { - var serviceBusClient = new ServiceBusClient(ConnectionString); - var serviceBusSender = serviceBusClient.CreateSender(QueueName); +Console.WriteLine("That's it, see you later!"); - for (int currentOrderAmount = 0; currentOrderAmount < requestedAmount; currentOrderAmount++) - { - var order = GenerateOrder(); - var rawOrder = JsonConvert.SerializeObject(order); - var orderMessage = new ServiceBusMessage(rawOrder); +static async Task QueueOrders(int requestedAmount, ServiceBusSender sender) +{ - Console.WriteLine($"Queuing order {order.Id} - A {order.ArticleNumber} for {order.Customer.FirstName} {order.Customer.LastName}"); - await serviceBusSender.SendMessageAsync(orderMessage); - } - } + for (int currentOrderAmount = 0; currentOrderAmount < requestedAmount; currentOrderAmount++) + { + var order = GenerateOrder(); + var rawOrder = JsonSerializer.Serialize(order); + var orderMessage = new ServiceBusMessage(rawOrder); - private static Order GenerateOrder() - { - var customerGenerator = new Faker() - .RuleFor(u => u.FirstName, (f, u) => f.Name.FirstName()) - .RuleFor(u => u.LastName, (f, u) => f.Name.LastName()); + Console.WriteLine($"Queuing order {order.Id} - A {order.ArticleNumber} for {order.Customer.FirstName} {order.Customer.LastName}"); + await sender.SendMessageAsync(orderMessage); + } +} - var orderGenerator = new Faker() - .RuleFor(u => u.Customer, () => customerGenerator) - .RuleFor(u => u.Id, f => Guid.NewGuid().ToString()) - .RuleFor(u => u.Amount, f => f.Random.Int()) - .RuleFor(u => u.ArticleNumber, f => f.Commerce.Product()); +static Order GenerateOrder() +{ + var customerGenerator = new Faker() + .RuleFor(u => u.FirstName, (f, u) => f.Name.FirstName()) + .RuleFor(u => u.LastName, (f, u) => f.Name.LastName()); - return orderGenerator.Generate(); - } + var orderGenerator = new Faker() + .RuleFor(u => u.Customer, () => customerGenerator) + .RuleFor(u => u.Id, f => Guid.NewGuid().ToString()) + .RuleFor(u => u.Amount, f => f.Random.Int()) + .RuleFor(u => u.ArticleNumber, f => f.Commerce.Product()); - private static int DetermineOrderAmount() - { - var rawAmount = Console.ReadLine(); - if (int.TryParse(rawAmount, out int amount)) - { - return amount; - } + return orderGenerator.Generate(); +} - Console.WriteLine("That's not a valid amount, let's try that again"); - return DetermineOrderAmount(); - } +static int DetermineOrderAmount() +{ + var rawAmount = Console.ReadLine(); + if (int.TryParse(rawAmount, out int amount)) + { + return amount; } + + Console.WriteLine("That's not a valid amount, let's try that again"); + return DetermineOrderAmount(); } diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json b/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json new file mode 100644 index 0000000..1454de2 --- /dev/null +++ b/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "OrderQueueOptions": { + "QueueName": "main", + "AuthMode": "WorkloadIdentity", + "ConnectionString": "test", + "FullyQualifiedNamespace": "", + "TenantId": "", + "ClientId": "b96d264b-7053-4465-a4a7-32be5b0fec49", + "ClientSecret": "", + } +} diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/Dockerfile b/src/Keda.Samples.Dotnet.OrderProcessor/Dockerfile index db4760b..29ccfdd 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/Dockerfile +++ b/src/Keda.Samples.Dotnet.OrderProcessor/Dockerfile @@ -1,7 +1,7 @@ -FROM mcr.microsoft.com/dotnet/core/runtime:3.1.1-alpine AS base +FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/core/sdk:3.1.101-alpine AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj", "Keda.Samples.Dotnet.OrderProcessor/"] COPY ["Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj", "Keda.Samples.Dotnet.Contracts/"] diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj b/src/Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj index 1da0eba..3b53ec7 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj +++ b/src/Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj @@ -1,16 +1,15 @@  - netcoreapp3.1 + net8.0 + 12 Linux - - - - - + + + diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/OrdersQueueProcessor.cs b/src/Keda.Samples.Dotnet.OrderProcessor/OrdersQueueProcessor.cs index 8cca951..3dab4ad 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/OrdersQueueProcessor.cs +++ b/src/Keda.Samples.Dotnet.OrderProcessor/OrdersQueueProcessor.cs @@ -2,26 +2,24 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Azure.Messaging.ServiceBus; using Keda.Samples.Dotnet.Contracts; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Keda.Samples.Dotnet.OrderProcessor { - public class OrdersQueueProcessor : QueueWorker + public class OrdersQueueProcessor(IServiceScopeFactory factory, ILogger logger) + : QueueWorker(factory, logger) { - public OrdersQueueProcessor(IConfiguration configuration, ILogger logger) - : base(configuration, logger) - { - } - protected override async Task ProcessMessage(Order order, string messageId, IReadOnlyDictionary userProperties, CancellationToken cancellationToken) { - Logger.LogInformation("Processing order {OrderId} for {OrderAmount} units of {OrderArticle} bought by {CustomerFirstName} {CustomerLastName}", order.Id, order.Amount, order.ArticleNumber, order.Customer.FirstName, order.Customer.LastName); + logger.LogInformation("Processing order {OrderId} for {OrderAmount} units of {OrderArticle} bought by {CustomerFirstName} {CustomerLastName}", order.Id, order.Amount, order.ArticleNumber, order.Customer.FirstName, order.Customer.LastName); await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); - Logger.LogInformation("Order {OrderId} processed", order.Id); + logger.LogInformation("Order {OrderId} processed", order.Id); } } } diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs b/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs index 74f9869..baf5f0f 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs @@ -1,30 +1,10 @@ -using Microsoft.Extensions.Configuration; +using Keda.Samples.Dotnet.Contracts; +using Keda.Samples.Dotnet.OrderProcessor; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -namespace Keda.Samples.Dotnet.OrderProcessor -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +var builder = Host.CreateApplicationBuilder(); +builder.Services.AddOrderQueueServices(); +builder.Services.AddHostedService(); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.AddEnvironmentVariables(); - }) - .ConfigureLogging((hostBuilderContext, loggingBuilder) => - { - loggingBuilder.AddConsole(consoleLoggerOptions => consoleLoggerOptions.TimestampFormat = "[HH:mm:ss]"); - }) - .ConfigureServices(services => - { - services.AddHostedService(); - }); - } -} +builder.Build().Run(); diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/QueueWorker.cs b/src/Keda.Samples.Dotnet.OrderProcessor/QueueWorker.cs index 2e17dcc..5d8c9d3 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/QueueWorker.cs +++ b/src/Keda.Samples.Dotnet.OrderProcessor/QueueWorker.cs @@ -1,124 +1,83 @@ using System; using System.Collections.Generic; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -namespace Keda.Samples.Dotnet.OrderProcessor -{ - public abstract class QueueWorker : BackgroundService - { - protected ILogger> Logger { get; } - protected IConfiguration Configuration { get; } +namespace Keda.Samples.Dotnet.OrderProcessor; - protected QueueWorker(IConfiguration configuration, ILogger> logger) - { - Configuration = configuration; - Logger = logger; - } +public abstract class QueueWorker( + IServiceScopeFactory serviceScopeFactory, + ILogger> logger) + : BackgroundService +{ - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - var queueName = Configuration.GetValue("KEDA_SERVICEBUS_QUEUE_NAME"); - var messageProcessor = CreateServiceBusProcessor(queueName); - messageProcessor.ProcessMessageAsync += HandleMessageAsync; - messageProcessor.ProcessErrorAsync += HandleReceivedExceptionAsync; + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + using var scope = serviceScopeFactory.CreateScope(); + + var messageProcessor = scope.ServiceProvider.GetRequiredService(); - Logger.LogInformation($"Starting message pump on queue {queueName} in namespace {messageProcessor.FullyQualifiedNamespace}"); - await messageProcessor.StartProcessingAsync(stoppingToken); - Logger.LogInformation("Message pump started"); + messageProcessor.ProcessMessageAsync += HandleMessageAsync; + messageProcessor.ProcessErrorAsync += HandleReceivedExceptionAsync; - while (!stoppingToken.IsCancellationRequested) - { - await Task.Delay(TimeSpan.FromSeconds(1)); - } - - Logger.LogInformation("Closing message pump"); - await messageProcessor.CloseAsync(cancellationToken: stoppingToken); - Logger.LogInformation("Message pump closed : {Time}", DateTimeOffset.UtcNow); - } + logger.LogInformation($"Starting message pump on queue {messageProcessor.EntityPath} in namespace {messageProcessor.FullyQualifiedNamespace}"); + await messageProcessor.StartProcessingAsync(stoppingToken); + logger.LogInformation("Message pump started"); - private ServiceBusProcessor CreateServiceBusProcessor(string queueName) + while (!stoppingToken.IsCancellationRequested) { - var serviceBusClient = AuthenticateToAzureServiceBus(); - var messageProcessor = serviceBusClient.CreateProcessor(queueName); - return messageProcessor; + await Task.Delay(TimeSpan.FromSeconds(1)); } - private ServiceBusClient AuthenticateToAzureServiceBus() + logger.LogInformation("Closing message pump"); + await messageProcessor.CloseAsync(cancellationToken: stoppingToken); + logger.LogInformation("Message pump closed : {Time}", DateTimeOffset.UtcNow); + } + + + private async Task HandleMessageAsync (ProcessMessageEventArgs processMessageEventArgs) + { + try { - var authenticationMode = Configuration.GetValue("KEDA_SERVICEBUS_AUTH_MODE"); - - ServiceBusClient serviceBusClient; + var rawMessageBody = Encoding.UTF8.GetString(processMessageEventArgs.Message.Body); + logger.LogInformation("Received message {MessageId} with body {MessageBody}", + processMessageEventArgs.Message.MessageId, rawMessageBody); - switch (authenticationMode) + var order = JsonSerializer.Deserialize(rawMessageBody); + if (order != null) { - case AuthenticationMode.ConnectionString: - Logger.LogInformation($"Authentication by using connection string"); - serviceBusClient = ServiceBusClientFactory.CreateWithConnectionStringAuthentication(Configuration); - break; - case AuthenticationMode.ServicePrinciple: - Logger.LogInformation("Authentication by using service principle"); - serviceBusClient = ServiceBusClientFactory.CreateWithServicePrincipleAuthentication(Configuration); - break; - case AuthenticationMode.PodIdentity: - Logger.LogInformation("Authentication by using pod identity"); - serviceBusClient = ServiceBusClientFactory.CreateWithPodIdentityAuthentication(Configuration, Logger); - break; - case AuthenticationMode.WorkloadIdentity: - Logger.LogInformation("Authentication by using workload identity"); - serviceBusClient = ServiceBusClientFactory.CreateWithWorkloadIdentityAuthentication(Configuration, Logger); - break; - default: - throw new ArgumentOutOfRangeException(); + await ProcessMessage(order, processMessageEventArgs.Message.MessageId, + processMessageEventArgs.Message.ApplicationProperties, + processMessageEventArgs.CancellationToken); } - - return serviceBusClient; - } - - private async Task HandleMessageAsync (ProcessMessageEventArgs processMessageEventArgs) - { - try + else { - var rawMessageBody = Encoding.UTF8.GetString(processMessageEventArgs.Message.Body.ToBytes().ToArray()); - Logger.LogInformation("Received message {MessageId} with body {MessageBody}", - processMessageEventArgs.Message.MessageId, rawMessageBody); - - var order = JsonConvert.DeserializeObject(rawMessageBody); - if (order != null) - { - await ProcessMessage(order, processMessageEventArgs.Message.MessageId, - processMessageEventArgs.Message.ApplicationProperties, - processMessageEventArgs.CancellationToken); - } - else - { - Logger.LogError( - "Unable to deserialize to message contract {ContractName} for message {MessageBody}", - typeof(TMessage), rawMessageBody); - } + logger.LogError( + "Unable to deserialize to message contract {ContractName} for message {MessageBody}", + typeof(TMessage), rawMessageBody); + } - Logger.LogInformation("Message {MessageId} processed", processMessageEventArgs.Message.MessageId); + logger.LogInformation("Message {MessageId} processed", processMessageEventArgs.Message.MessageId); - await processMessageEventArgs.CompleteMessageAsync(processMessageEventArgs.Message); - } - catch (Exception ex) - { - Logger.LogError(ex, "Unable to handle message"); - } + await processMessageEventArgs.CompleteMessageAsync(processMessageEventArgs.Message); } - - private Task HandleReceivedExceptionAsync(ProcessErrorEventArgs exceptionEvent) + catch (Exception ex) { - Logger.LogError(exceptionEvent.Exception, "Unable to process message"); - return Task.CompletedTask; + logger.LogError(ex, "Unable to handle message"); } + } - protected abstract Task ProcessMessage(TMessage order, string messageId, IReadOnlyDictionary userProperties, CancellationToken cancellationToken); + private Task HandleReceivedExceptionAsync(ProcessErrorEventArgs exceptionEvent) + { + logger.LogError(exceptionEvent.Exception, "Unable to process message"); + return Task.CompletedTask; } -} + + protected abstract Task ProcessMessage(TMessage order, string messageId, IReadOnlyDictionary userProperties, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/ServiceBusClientFactory.cs b/src/Keda.Samples.Dotnet.OrderProcessor/ServiceBusClientFactory.cs deleted file mode 100644 index 89cc542..0000000 --- a/src/Keda.Samples.Dotnet.OrderProcessor/ServiceBusClientFactory.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Azure.Identity; -using Azure.Messaging.ServiceBus; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Keda.Samples.Dotnet.OrderProcessor -{ - public static class ServiceBusClientFactory - { - public static ServiceBusClient CreateWithPodIdentityAuthentication(IConfiguration configuration, ILogger logger) - { - var hostname = configuration.GetValue("KEDA_SERVICEBUS_HOST_NAME"); - - var clientIdentityId = configuration.GetValue("KEDA_SERVICEBUS_IDENTITY_USERASSIGNEDID", defaultValue: null); - if (string.IsNullOrWhiteSpace(clientIdentityId) == false) - { - logger.LogInformation("Using user-assigned identity with ID {UserAssignedIdentityId}", clientIdentityId); - } - - return new ServiceBusClient(hostname, new ManagedIdentityCredential(clientId: clientIdentityId)); - } - - public static ServiceBusClient CreateWithWorkloadIdentityAuthentication(IConfiguration configuration, ILogger logger) - { - var hostname = configuration.GetValue("KEDA_SERVICEBUS_HOST_NAME"); - - return new ServiceBusClient(hostname, new ManagedIdentityCredential()); - } - - public static ServiceBusClient CreateWithServicePrincipleAuthentication(IConfiguration configuration) - { - var hostname = configuration.GetValue("KEDA_SERVICEBUS_HOST_NAME"); - var tenantId = configuration.GetValue("KEDA_SERVICEBUS_TENANT_ID"); - var appIdentityId = configuration.GetValue("KEDA_SERVICEBUS_IDENTITY_APPID"); - var appIdentitySecret = configuration.GetValue("KEDA_SERVICEBUS_IDENTITY_SECRET"); - - return new ServiceBusClient(hostname, new ClientSecretCredential(tenantId, appIdentityId, appIdentitySecret)); - } - - public static ServiceBusClient CreateWithConnectionStringAuthentication(IConfiguration configuration) - { - var connectionString = configuration.GetValue("KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING"); - return new ServiceBusClient(connectionString); - } - } -} diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json index 8983e0f..f647e02 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json +++ b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json @@ -5,5 +5,14 @@ "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } + }, + "OrderQueueOptions": { + "QueueName": "main", + "AuthMode": "WorkloadIdentity", + "ConnectionString": "", + "FullyQualifiedNamespace": "", + "TenantId": "", + "ClientId": "b96d264b-7053-4465-a4a7-32be5b0fec49", + "ClientSecret": "", } } diff --git a/src/global.json b/src/global.json new file mode 100644 index 0000000..87932f4 --- /dev/null +++ b/src/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "rollForward": "latestMajor", + "allowPrerelease": true + } +} \ No newline at end of file From ed91bff96132ef0e3e8fe696e10c579f32b6f073 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Tue, 4 Jun 2024 15:18:12 +0200 Subject: [PATCH 02/11] cleanup dependency injection --- src/Keda.Samples.DotNet.Web/Program.cs | 4 ++-- src/Keda.Samples.DotNet.Web/appsettings.json | 8 +++---- .../AddServiceBusClientExtension.cs | 10 ++++++--- .../Program.cs | 2 +- .../appsettings.json | 10 ++++----- .../OrdersQueueProcessor.cs | 3 +-- .../Program.cs | 3 ++- .../QueueWorker.cs | 21 +++++++------------ .../appsettings.json | 8 +++---- src/global.json | 6 ------ 10 files changed, 33 insertions(+), 42 deletions(-) delete mode 100644 src/global.json diff --git a/src/Keda.Samples.DotNet.Web/Program.cs b/src/Keda.Samples.DotNet.Web/Program.cs index 602a13e..62fce2c 100644 --- a/src/Keda.Samples.DotNet.Web/Program.cs +++ b/src/Keda.Samples.DotNet.Web/Program.cs @@ -5,8 +5,8 @@ using Microsoft.Extensions.Logging; var builder = WebApplication.CreateBuilder(); - -builder.Services.AddLogging(builder => builder.AddConsole()); +builder.Services.AddOptions().BindConfiguration(nameof(OrderQueueOptions)); +builder.Services.AddLogging(lb => lb.AddConsole()); builder.Services.AddControllers(); builder.Services.AddRazorPages(); builder.Services.AddSignalR(); diff --git a/src/Keda.Samples.DotNet.Web/appsettings.json b/src/Keda.Samples.DotNet.Web/appsettings.json index 3cf13cd..c5b0924 100644 --- a/src/Keda.Samples.DotNet.Web/appsettings.json +++ b/src/Keda.Samples.DotNet.Web/appsettings.json @@ -9,11 +9,11 @@ "AllowedHosts": "*", "OrderQueueOptions": { "QueueName": "main", - "AuthMode": "WorkloadIdentity", + "AuthMode": "AzureDefaultCredential", + "FullyQualifiedNamespace": "rihag-deleteme-edc23.servicebus.windows.net", "ConnectionString": "", - "FullyQualifiedNamespace": "", "TenantId": "", - "ClientId": "b96d264b-7053-4465-a4a7-32be5b0fec49", - "ClientSecret": "", + "ClientId": "", + "ClientSecret": "" } } diff --git a/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs b/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs index 666f172..176f0d4 100644 --- a/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs +++ b/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs @@ -3,7 +3,6 @@ using Azure.Identity; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; @@ -45,6 +44,9 @@ private static ServiceBusClient AuthenticateToAzureServiceBus(OrderQueueOptions { switch (options.AuthMode) { + case AuthenticationMode.AzureDefaultCredential: + logger.LogInformation($"Authentication with Azure Default Credential"); + return new ServiceBusClient(options.FullyQualifiedNamespace, new DefaultAzureCredential()); case AuthenticationMode.ConnectionString: logger.LogInformation($"Authentication by using connection string"); return new ServiceBusClient(options.ConnectionString); @@ -65,10 +67,11 @@ private static ServiceBusClient AuthenticateToAzureServiceBus(OrderQueueOptions public enum AuthenticationMode { + AzureDefaultCredential, ConnectionString, ServicePrinciple, PodIdentity, - WorkloadIdentity + WorkloadIdentity, } public class OrderQueueOptions { @@ -76,8 +79,9 @@ public class OrderQueueOptions public AuthenticationMode AuthMode { get; set; } [Required] public string QueueName { get; set; } - + public string ConnectionString { get; set; } + [Required] public string FullyQualifiedNamespace { get; set;} public string TenantId { get; set;} public string ClientId { get; set;} diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs index d803349..6ca5dc6 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Hosting; var builder = Host.CreateApplicationBuilder(); -builder.Services.Configure(builder.Configuration.GetSection(key: nameof(OrderQueueOptions))); +builder.Services.AddOptions().Bind(builder.Configuration.GetSection(nameof(OrderQueueOptions))); builder.Services.AddOrderQueueServices(); var app = builder.Build(); var sender = app.Services.GetRequiredService(); diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json b/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json index 1454de2..75b7229 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json +++ b/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json @@ -8,11 +8,11 @@ }, "OrderQueueOptions": { "QueueName": "main", - "AuthMode": "WorkloadIdentity", - "ConnectionString": "test", - "FullyQualifiedNamespace": "", + "AuthMode": "AzureDefaultCredential", + "FullyQualifiedNamespace": "rihag-deleteme-edc23.servicebus.windows.net", + "ConnectionString": "", "TenantId": "", - "ClientId": "b96d264b-7053-4465-a4a7-32be5b0fec49", - "ClientSecret": "", + "ClientId": "", + "ClientSecret": "" } } diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/OrdersQueueProcessor.cs b/src/Keda.Samples.Dotnet.OrderProcessor/OrdersQueueProcessor.cs index 3dab4ad..79e3f4c 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/OrdersQueueProcessor.cs +++ b/src/Keda.Samples.Dotnet.OrderProcessor/OrdersQueueProcessor.cs @@ -10,8 +10,7 @@ namespace Keda.Samples.Dotnet.OrderProcessor { - public class OrdersQueueProcessor(IServiceScopeFactory factory, ILogger logger) - : QueueWorker(factory, logger) + public class OrdersQueueProcessor(ServiceBusProcessor processor, ILogger logger) : QueueWorker(processor, logger) { protected override async Task ProcessMessage(Order order, string messageId, IReadOnlyDictionary userProperties, CancellationToken cancellationToken) { diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs b/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs index baf5f0f..cf66fae 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs @@ -4,7 +4,8 @@ using Microsoft.Extensions.Hosting; var builder = Host.CreateApplicationBuilder(); +builder.Services.AddOptions().BindConfiguration(nameof(OrderQueueOptions)); builder.Services.AddOrderQueueServices(); builder.Services.AddHostedService(); -builder.Build().Run(); +builder.Build().Run(); \ No newline at end of file diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/QueueWorker.cs b/src/Keda.Samples.Dotnet.OrderProcessor/QueueWorker.cs index 5d8c9d3..b7bd81f 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/QueueWorker.cs +++ b/src/Keda.Samples.Dotnet.OrderProcessor/QueueWorker.cs @@ -5,29 +5,21 @@ using System.Threading; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Keda.Samples.Dotnet.OrderProcessor; -public abstract class QueueWorker( - IServiceScopeFactory serviceScopeFactory, - ILogger> logger) - : BackgroundService +public abstract class QueueWorker(ServiceBusProcessor processor, ILogger> logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - using var scope = serviceScopeFactory.CreateScope(); - - var messageProcessor = scope.ServiceProvider.GetRequiredService(); - - messageProcessor.ProcessMessageAsync += HandleMessageAsync; - messageProcessor.ProcessErrorAsync += HandleReceivedExceptionAsync; + processor.ProcessMessageAsync += HandleMessageAsync; + processor.ProcessErrorAsync += HandleReceivedExceptionAsync; - logger.LogInformation($"Starting message pump on queue {messageProcessor.EntityPath} in namespace {messageProcessor.FullyQualifiedNamespace}"); - await messageProcessor.StartProcessingAsync(stoppingToken); + logger.LogInformation($"Starting message pump on queue {processor.EntityPath} in namespace {processor.FullyQualifiedNamespace}"); + await processor.StartProcessingAsync(stoppingToken); logger.LogInformation("Message pump started"); while (!stoppingToken.IsCancellationRequested) @@ -36,7 +28,8 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } logger.LogInformation("Closing message pump"); - await messageProcessor.CloseAsync(cancellationToken: stoppingToken); + await processor.CloseAsync(CancellationToken.None); + await processor.DisposeAsync(); logger.LogInformation("Message pump closed : {Time}", DateTimeOffset.UtcNow); } diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json index f647e02..75b7229 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json +++ b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json @@ -8,11 +8,11 @@ }, "OrderQueueOptions": { "QueueName": "main", - "AuthMode": "WorkloadIdentity", + "AuthMode": "AzureDefaultCredential", + "FullyQualifiedNamespace": "rihag-deleteme-edc23.servicebus.windows.net", "ConnectionString": "", - "FullyQualifiedNamespace": "", "TenantId": "", - "ClientId": "b96d264b-7053-4465-a4a7-32be5b0fec49", - "ClientSecret": "", + "ClientId": "", + "ClientSecret": "" } } diff --git a/src/global.json b/src/global.json deleted file mode 100644 index 87932f4..0000000 --- a/src/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "rollForward": "latestMajor", - "allowPrerelease": true - } -} \ No newline at end of file From 96fa56c629a13d45bb80d4623fc5dcb67d47589a Mon Sep 17 00:00:00 2001 From: Richard87 Date: Tue, 4 Jun 2024 15:38:45 +0200 Subject: [PATCH 03/11] Cleanup generate orders --- .../Controllers/OrdersController.cs | 1 - .../Controllers/QueueController.cs | 5 +- src/Keda.Samples.DotNet.Web/Program.cs | 5 ++ src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs | 5 ++ src/Keda.Samples.Dotnet.Contracts/Customer.cs | 9 ---- src/Keda.Samples.Dotnet.Contracts/Order.cs | 10 ---- .../QueueStatus.cs | 6 --- .../Program.cs | 49 ++++++------------- 8 files changed, 27 insertions(+), 63 deletions(-) create mode 100644 src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs delete mode 100644 src/Keda.Samples.Dotnet.Contracts/Customer.cs delete mode 100644 src/Keda.Samples.Dotnet.Contracts/Order.cs delete mode 100644 src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs diff --git a/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs b/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs index 7eb1d2c..3474575 100644 --- a/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs +++ b/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs @@ -26,7 +26,6 @@ public async Task Create([FromBody, Required] Order order, [FromS { var jsonString = JsonSerializer.Serialize(order); var orderMessage = new ServiceBusMessage(jsonString); - await sender.SendMessageAsync(orderMessage, ct); return Accepted(); diff --git a/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs b/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs index adfd661..0332cdb 100644 --- a/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs +++ b/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs @@ -12,10 +12,7 @@ public class QueueController : ControllerBase [ApiExplorerSettings(IgnoreApi = true)] public QueueStatus Get([FromServices] ServiceBusReceiver receiver) { - return new QueueStatus - { - MessageCount = receiver.PrefetchCount - }; + return new QueueStatus(receiver.PrefetchCount); } } } diff --git a/src/Keda.Samples.DotNet.Web/Program.cs b/src/Keda.Samples.DotNet.Web/Program.cs index 62fce2c..ea875d4 100644 --- a/src/Keda.Samples.DotNet.Web/Program.cs +++ b/src/Keda.Samples.DotNet.Web/Program.cs @@ -1,5 +1,9 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading; +using Azure.Messaging.ServiceBus; using Keda.Samples.Dotnet.Contracts; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -31,4 +35,5 @@ swaggerUiOptions.DocumentTitle = "KEDA API"; }); + await app.RunAsync(); diff --git a/src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs b/src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs new file mode 100644 index 0000000..295d8f6 --- /dev/null +++ b/src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs @@ -0,0 +1,5 @@ +namespace Keda.Samples.Dotnet.Contracts; + +public record Customer(string FirstName, string LastName); +public record Order(string Id, int Amount, string ArticleNumber, Customer Customer); +public record QueueStatus(long MessageCount); \ No newline at end of file diff --git a/src/Keda.Samples.Dotnet.Contracts/Customer.cs b/src/Keda.Samples.Dotnet.Contracts/Customer.cs deleted file mode 100644 index b54e674..0000000 --- a/src/Keda.Samples.Dotnet.Contracts/Customer.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Keda.Samples.Dotnet.Contracts -{ - public class Customer - { - public string FirstName { get; set; } - - public string LastName { get; set; } - } -} \ No newline at end of file diff --git a/src/Keda.Samples.Dotnet.Contracts/Order.cs b/src/Keda.Samples.Dotnet.Contracts/Order.cs deleted file mode 100644 index f08de1c..0000000 --- a/src/Keda.Samples.Dotnet.Contracts/Order.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Keda.Samples.Dotnet.Contracts -{ - public class Order - { - public string Id { get; set; } - public int Amount { get; set; } - public string ArticleNumber { get; set; } - public Customer Customer { get; set; } - } -} \ No newline at end of file diff --git a/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs b/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs deleted file mode 100644 index 4f4def3..0000000 --- a/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Keda.Samples.Dotnet.Contracts; - -public class QueueStatus -{ - public long MessageCount { get; set; } -} \ No newline at end of file diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs index 6ca5dc6..cd2bd0a 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs @@ -2,7 +2,6 @@ using Keda.Samples.Dotnet.Contracts; using System; using System.Text.Json; -using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -14,49 +13,33 @@ var sender = app.Services.GetRequiredService(); Console.WriteLine("Let's queue some orders, how many do you want?"); +var orderCount = ReadNumber(); -var requestedAmount = DetermineOrderAmount(); -await QueueOrders(requestedAmount, sender); - -Console.WriteLine("That's it, see you later!"); - -static async Task QueueOrders(int requestedAmount, ServiceBusSender sender) +for (var i = 0; i < orderCount; i++) { - - for (int currentOrderAmount = 0; currentOrderAmount < requestedAmount; currentOrderAmount++) - { - var order = GenerateOrder(); - var rawOrder = JsonSerializer.Serialize(order); - var orderMessage = new ServiceBusMessage(rawOrder); - - Console.WriteLine($"Queuing order {order.Id} - A {order.ArticleNumber} for {order.Customer.FirstName} {order.Customer.LastName}"); - await sender.SendMessageAsync(orderMessage); - } + var f = new Faker(); + var customer = new Customer(f.Name.FirstName(), f.Name.LastName()); + var order = new Order(Guid.NewGuid().ToString(),f.Random.Int(),f.Commerce.Product(), customer); + + var rawOrder = JsonSerializer.Serialize(order); + var orderMessage = new ServiceBusMessage(rawOrder); + + Console.WriteLine($"Queuing order {order.Id} - A {order.ArticleNumber} for {order.Customer.FirstName} {order.Customer.LastName}"); + await sender.SendMessageAsync(orderMessage); } -static Order GenerateOrder() -{ - var customerGenerator = new Faker() - .RuleFor(u => u.FirstName, (f, u) => f.Name.FirstName()) - .RuleFor(u => u.LastName, (f, u) => f.Name.LastName()); - - var orderGenerator = new Faker() - .RuleFor(u => u.Customer, () => customerGenerator) - .RuleFor(u => u.Id, f => Guid.NewGuid().ToString()) - .RuleFor(u => u.Amount, f => f.Random.Int()) - .RuleFor(u => u.ArticleNumber, f => f.Commerce.Product()); +Console.WriteLine("That's it, see you later!"); +return; - return orderGenerator.Generate(); -} -static int DetermineOrderAmount() +static int ReadNumber() { var rawAmount = Console.ReadLine(); - if (int.TryParse(rawAmount, out int amount)) + if (int.TryParse(rawAmount, out var amount)) { return amount; } Console.WriteLine("That's not a valid amount, let's try that again"); - return DetermineOrderAmount(); + return ReadNumber(); } From bb5ecb75beecefce16c4ed5d2bc855337d310bd8 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Wed, 5 Jun 2024 09:36:47 +0200 Subject: [PATCH 04/11] cleanup swagger config --- .../Controllers/OrdersController.cs | 33 ------------------- .../Controllers/QueueController.cs | 18 ---------- .../AddSwaggerServicesExtensions.cs | 25 -------------- .../Keda.Samples.DotNet.Web.csproj | 1 - src/Keda.Samples.DotNet.Web/Program.cs | 26 ++++++++++----- 5 files changed, 18 insertions(+), 85 deletions(-) delete mode 100644 src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs delete mode 100644 src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs delete mode 100644 src/Keda.Samples.DotNet.Web/Extensions/AddSwaggerServicesExtensions.cs diff --git a/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs b/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs deleted file mode 100644 index 3474575..0000000 --- a/src/Keda.Samples.DotNet.Web/Controllers/OrdersController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Azure.Messaging.ServiceBus; -using Keda.Samples.Dotnet.Contracts; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Keda.Samples.DotNet.Web.Controllers; - -/// -/// API endpoint to manage orders -/// -[ApiController] -[Route("api/v1/orders")] -public class OrdersController : ControllerBase -{ - - /// - /// Create Order - /// - [HttpPost(Name = "Order_Create")] - [ProducesResponseType(StatusCodes.Status201Created)] - public async Task Create([FromBody, Required] Order order, [FromServices] ServiceBusSender sender, CancellationToken ct) - { - var jsonString = JsonSerializer.Serialize(order); - var orderMessage = new ServiceBusMessage(jsonString); - await sender.SendMessageAsync(orderMessage, ct); - - return Accepted(); - } -} \ No newline at end of file diff --git a/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs b/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs deleted file mode 100644 index 0332cdb..0000000 --- a/src/Keda.Samples.DotNet.Web/Controllers/QueueController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Azure.Messaging.ServiceBus; -using Keda.Samples.Dotnet.Contracts; -using Microsoft.AspNetCore.Mvc; - -namespace Keda.Samples.DotNet.Web.Controllers -{ - [ApiController] - [Route("api/v1/[controller]")] - public class QueueController : ControllerBase - { - [HttpGet] - [ApiExplorerSettings(IgnoreApi = true)] - public QueueStatus Get([FromServices] ServiceBusReceiver receiver) - { - return new QueueStatus(receiver.PrefetchCount); - } - } -} diff --git a/src/Keda.Samples.DotNet.Web/Extensions/AddSwaggerServicesExtensions.cs b/src/Keda.Samples.DotNet.Web/Extensions/AddSwaggerServicesExtensions.cs deleted file mode 100644 index 0fa919a..0000000 --- a/src/Keda.Samples.DotNet.Web/Extensions/AddSwaggerServicesExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.IO; -using Microsoft.OpenApi.Models; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class AddSwaggerServicesExtensions - { - public static void AddSwagger(this IServiceCollection services) - { - var openApiInformation = new OpenApiInfo - { - Title = "KEDA API", - Version = "v1" - }; - - services.AddSwaggerGen(swaggerGenerationOptions => - { - swaggerGenerationOptions.SwaggerDoc("v1", openApiInformation); - swaggerGenerationOptions.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, - "Keda.Samples.DotNet.Web.Open-Api.xml")); - }); - } - } -} \ No newline at end of file diff --git a/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj b/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj index 8fef638..dfad7ce 100644 --- a/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj +++ b/src/Keda.Samples.DotNet.Web/Keda.Samples.DotNet.Web.csproj @@ -6,7 +6,6 @@ ..\docker-compose.dcproj 858adc6c-c378-4698-a3c4-1112a29d6561 true - Keda.Samples.DotNet.Web.Open-Api.xml Linux diff --git a/src/Keda.Samples.DotNet.Web/Program.cs b/src/Keda.Samples.DotNet.Web/Program.cs index ea875d4..caf65f7 100644 --- a/src/Keda.Samples.DotNet.Web/Program.cs +++ b/src/Keda.Samples.DotNet.Web/Program.cs @@ -1,8 +1,10 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json; using System.Threading; using Azure.Messaging.ServiceBus; using Keda.Samples.Dotnet.Contracts; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -14,7 +16,8 @@ builder.Services.AddControllers(); builder.Services.AddRazorPages(); builder.Services.AddSignalR(); -builder.Services.AddSwagger(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); builder.Services.AddOrderQueueServices(); @@ -26,14 +29,21 @@ app.UseRouting(); app.UseAuthorization(); app.MapRazorPages(); -app.MapControllers(); - app.UseSwagger(); -app.UseSwaggerUI(swaggerUiOptions => -{ - swaggerUiOptions.SwaggerEndpoint("v1/swagger.json", "Keda.Samples.Dotnet.API"); - swaggerUiOptions.DocumentTitle = "KEDA API"; -}); +app.UseSwaggerUI(); + +app.MapGet("/api/v1/queue", + async ([FromServices] ServiceBusReceiver receiver, CancellationToken ct) => + new QueueStatus((await receiver.PeekMessagesAsync(100, null, ct)).Count)); + +app.MapPost("api/v1/orders", + async ([FromBody, Required] Order order, [FromServices] ServiceBusSender sender, CancellationToken ct) => + { + var jsonString = JsonSerializer.Serialize(order); + var orderMessage = new ServiceBusMessage(jsonString); + await sender.SendMessageAsync(orderMessage, ct); + return TypedResults.Accepted(order.Id); + }).WithName("Order_Create"); await app.RunAsync(); From 236be80c7c405bd5edaa7bb0363acc6b63df380f Mon Sep 17 00:00:00 2001 From: Richard87 Date: Wed, 5 Jun 2024 09:39:45 +0200 Subject: [PATCH 05/11] remove classes --- .../Pages/Error.cshtml | 8 ----- .../Pages/Error.cshtml.cs | 31 ------------------- .../Pages/Index.cshtml.cs | 19 ------------ .../Pages/Privacy.cshtml | 1 - .../Pages/Privacy.cshtml.cs | 24 -------------- 5 files changed, 83 deletions(-) delete mode 100644 src/Keda.Samples.DotNet.Web/Pages/Error.cshtml.cs delete mode 100644 src/Keda.Samples.DotNet.Web/Pages/Index.cshtml.cs delete mode 100644 src/Keda.Samples.DotNet.Web/Pages/Privacy.cshtml.cs diff --git a/src/Keda.Samples.DotNet.Web/Pages/Error.cshtml b/src/Keda.Samples.DotNet.Web/Pages/Error.cshtml index 6f92b95..317d91e 100644 --- a/src/Keda.Samples.DotNet.Web/Pages/Error.cshtml +++ b/src/Keda.Samples.DotNet.Web/Pages/Error.cshtml @@ -1,5 +1,4 @@ @page -@model ErrorModel @{ ViewData["Title"] = "Error"; } @@ -7,13 +6,6 @@

Error.

An error occurred while processing your request.

-@if (Model.ShowRequestId) -{ -

- Request ID: @Model.RequestId -

-} -

Development Mode

Swapping to the Development environment displays detailed information about the error that occurred. diff --git a/src/Keda.Samples.DotNet.Web/Pages/Error.cshtml.cs b/src/Keda.Samples.DotNet.Web/Pages/Error.cshtml.cs deleted file mode 100644 index 864ca9b..0000000 --- a/src/Keda.Samples.DotNet.Web/Pages/Error.cshtml.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace Keda.Samples.DotNet.Web.Pages -{ - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public class ErrorModel : PageModel - { - public string RequestId { get; set; } - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - private readonly ILogger _logger; - - public ErrorModel(ILogger logger) - { - _logger = logger; - } - - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } - } -} diff --git a/src/Keda.Samples.DotNet.Web/Pages/Index.cshtml.cs b/src/Keda.Samples.DotNet.Web/Pages/Index.cshtml.cs deleted file mode 100644 index 59ee267..0000000 --- a/src/Keda.Samples.DotNet.Web/Pages/Index.cshtml.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace Keda.Samples.DotNet.Web.Pages -{ - public class IndexModel : PageModel - { - private readonly ILogger _logger; - - public IndexModel(ILogger logger) - { - _logger = logger; - } - - public void OnGet() - { - } - } -} diff --git a/src/Keda.Samples.DotNet.Web/Pages/Privacy.cshtml b/src/Keda.Samples.DotNet.Web/Pages/Privacy.cshtml index 46ba966..5c36d09 100644 --- a/src/Keda.Samples.DotNet.Web/Pages/Privacy.cshtml +++ b/src/Keda.Samples.DotNet.Web/Pages/Privacy.cshtml @@ -1,5 +1,4 @@ @page -@model PrivacyModel @{ ViewData["Title"] = "Privacy Policy"; } diff --git a/src/Keda.Samples.DotNet.Web/Pages/Privacy.cshtml.cs b/src/Keda.Samples.DotNet.Web/Pages/Privacy.cshtml.cs deleted file mode 100644 index 3c6aa4f..0000000 --- a/src/Keda.Samples.DotNet.Web/Pages/Privacy.cshtml.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace Keda.Samples.DotNet.Web.Pages -{ - public class PrivacyModel : PageModel - { - private readonly ILogger _logger; - - public PrivacyModel(ILogger logger) - { - _logger = logger; - } - - public void OnGet() - { - } - } -} From 940be9923f03c4ca91d610159907c1c4db740d15 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Wed, 5 Jun 2024 09:49:39 +0200 Subject: [PATCH 06/11] fix dependencies --- .../Keda.Samples.Dotnet.Contracts.csproj | 40 +++++++------------ .../Keda.Samples.Dotnet.OrderProcessor.csproj | 1 + .../appsettings.Development.json | 9 ----- 3 files changed, 15 insertions(+), 35 deletions(-) delete mode 100644 src/Keda.Samples.Dotnet.OrderProcessor/appsettings.Development.json diff --git a/src/Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj b/src/Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj index a8e71ef..bc69f45 100644 --- a/src/Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj +++ b/src/Keda.Samples.Dotnet.Contracts/Keda.Samples.Dotnet.Contracts.csproj @@ -1,31 +1,19 @@ - - net8.0 - 12 - + + net8.0 + 12 + - - - - - - - + + + + + + + + + + - - - - ..\..\..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\8.0.6\Microsoft.Extensions.Configuration.dll - - - - ..\..\..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\8.0.6\Microsoft.Extensions.Hosting.dll - - - ..\..\..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\8.0.6\Microsoft.Extensions.Hosting.Abstractions.dll - - - - diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj b/src/Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj index 3b53ec7..7711a9a 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj +++ b/src/Keda.Samples.Dotnet.OrderProcessor/Keda.Samples.Dotnet.OrderProcessor.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.Development.json b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.Development.json deleted file mode 100644 index e203e94..0000000 --- a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - } -} From e09e745e9cf47c4e15fae14aabb662051077cc58 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Wed, 5 Jun 2024 10:38:09 +0200 Subject: [PATCH 07/11] add support for appsettings.local.json --- .gitignore | 10 +++++----- src/Keda.Samples.DotNet.Web/Program.cs | 2 ++ src/Keda.Samples.DotNet.Web/appsettings.json | 4 ++-- src/Keda.Samples.Dotnet.OrderGenerator/Program.cs | 4 +++- .../appsettings.json | 5 +++-- src/Keda.Samples.Dotnet.OrderProcessor/Program.cs | 4 +++- .../appsettings.json | 5 +++-- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 4f59a06..d49f84e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ *.user *.userosscache *.sln.docstates - +*/**/appsettings.local.json # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -221,7 +221,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -317,7 +317,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -326,6 +326,6 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ -/src/docker-compose.override.yml \ No newline at end of file +/src/docker-compose.override.yml diff --git a/src/Keda.Samples.DotNet.Web/Program.cs b/src/Keda.Samples.DotNet.Web/Program.cs index caf65f7..dc0dff9 100644 --- a/src/Keda.Samples.DotNet.Web/Program.cs +++ b/src/Keda.Samples.DotNet.Web/Program.cs @@ -6,11 +6,13 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; var builder = WebApplication.CreateBuilder(); +builder.Configuration.AddJsonFile("appsettings.local.json", optional: true).AddEnvironmentVariables(); builder.Services.AddOptions().BindConfiguration(nameof(OrderQueueOptions)); builder.Services.AddLogging(lb => lb.AddConsole()); builder.Services.AddControllers(); diff --git a/src/Keda.Samples.DotNet.Web/appsettings.json b/src/Keda.Samples.DotNet.Web/appsettings.json index c5b0924..02116dc 100644 --- a/src/Keda.Samples.DotNet.Web/appsettings.json +++ b/src/Keda.Samples.DotNet.Web/appsettings.json @@ -8,9 +8,9 @@ }, "AllowedHosts": "*", "OrderQueueOptions": { - "QueueName": "main", + "QueueName": "orders", "AuthMode": "AzureDefaultCredential", - "FullyQualifiedNamespace": "rihag-deleteme-edc23.servicebus.windows.net", + "FullyQualifiedNamespace": ".servicebus.windows.net", "ConnectionString": "", "TenantId": "", "ClientId": "", diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs index cd2bd0a..65c3279 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs @@ -3,10 +3,12 @@ using System; using System.Text.Json; using Azure.Messaging.ServiceBus; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; var builder = Host.CreateApplicationBuilder(); +builder.Configuration.AddJsonFile("appsettings.local.json", optional: true).AddEnvironmentVariables(); builder.Services.AddOptions().Bind(builder.Configuration.GetSection(nameof(OrderQueueOptions))); builder.Services.AddOrderQueueServices(); var app = builder.Build(); @@ -20,7 +22,7 @@ var f = new Faker(); var customer = new Customer(f.Name.FirstName(), f.Name.LastName()); var order = new Order(Guid.NewGuid().ToString(),f.Random.Int(),f.Commerce.Product(), customer); - + var rawOrder = JsonSerializer.Serialize(order); var orderMessage = new ServiceBusMessage(rawOrder); diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json b/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json index 75b7229..02116dc 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json +++ b/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json @@ -6,10 +6,11 @@ "Microsoft.Hosting.Lifetime": "Information" } }, + "AllowedHosts": "*", "OrderQueueOptions": { - "QueueName": "main", + "QueueName": "orders", "AuthMode": "AzureDefaultCredential", - "FullyQualifiedNamespace": "rihag-deleteme-edc23.servicebus.windows.net", + "FullyQualifiedNamespace": ".servicebus.windows.net", "ConnectionString": "", "TenantId": "", "ClientId": "", diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs b/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs index cf66fae..8deb6f9 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs @@ -1,11 +1,13 @@ using Keda.Samples.Dotnet.Contracts; using Keda.Samples.Dotnet.OrderProcessor; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; var builder = Host.CreateApplicationBuilder(); +builder.Configuration.AddJsonFile("appsettings.local.json", optional: true).AddEnvironmentVariables(); builder.Services.AddOptions().BindConfiguration(nameof(OrderQueueOptions)); builder.Services.AddOrderQueueServices(); builder.Services.AddHostedService(); -builder.Build().Run(); \ No newline at end of file +builder.Build().Run(); diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json index 75b7229..02116dc 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json +++ b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json @@ -6,10 +6,11 @@ "Microsoft.Hosting.Lifetime": "Information" } }, + "AllowedHosts": "*", "OrderQueueOptions": { - "QueueName": "main", + "QueueName": "orders", "AuthMode": "AzureDefaultCredential", - "FullyQualifiedNamespace": "rihag-deleteme-edc23.servicebus.windows.net", + "FullyQualifiedNamespace": ".servicebus.windows.net", "ConnectionString": "", "TenantId": "", "ClientId": "", From 51d7a27d4102c851128a35003b226e97063963b5 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Wed, 5 Jun 2024 12:35:53 +0200 Subject: [PATCH 08/11] remove env method call --- src/Keda.Samples.DotNet.Web/Program.cs | 2 +- src/Keda.Samples.Dotnet.OrderGenerator/Program.cs | 3 ++- src/Keda.Samples.Dotnet.OrderProcessor/Program.cs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Keda.Samples.DotNet.Web/Program.cs b/src/Keda.Samples.DotNet.Web/Program.cs index dc0dff9..6beba38 100644 --- a/src/Keda.Samples.DotNet.Web/Program.cs +++ b/src/Keda.Samples.DotNet.Web/Program.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; var builder = WebApplication.CreateBuilder(); -builder.Configuration.AddJsonFile("appsettings.local.json", optional: true).AddEnvironmentVariables(); +builder.Configuration.AddJsonFile("appsettings.local.json", optional: true); builder.Services.AddOptions().BindConfiguration(nameof(OrderQueueOptions)); builder.Services.AddLogging(lb => lb.AddConsole()); builder.Services.AddControllers(); diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs index 65c3279..5b09ae5 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderGenerator/Program.cs @@ -6,9 +6,10 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; var builder = Host.CreateApplicationBuilder(); -builder.Configuration.AddJsonFile("appsettings.local.json", optional: true).AddEnvironmentVariables(); +builder.Configuration.AddJsonFile("appsettings.local.json", optional: true); builder.Services.AddOptions().Bind(builder.Configuration.GetSection(nameof(OrderQueueOptions))); builder.Services.AddOrderQueueServices(); var app = builder.Build(); diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs b/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs index 8deb6f9..0cbc84c 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs +++ b/src/Keda.Samples.Dotnet.OrderProcessor/Program.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Hosting; var builder = Host.CreateApplicationBuilder(); -builder.Configuration.AddJsonFile("appsettings.local.json", optional: true).AddEnvironmentVariables(); +builder.Configuration.AddJsonFile("appsettings.local.json", optional: true); builder.Services.AddOptions().BindConfiguration(nameof(OrderQueueOptions)); builder.Services.AddOrderQueueServices(); builder.Services.AddHostedService(); From b6a6c004f1da3172609350aca8f5ea37f9edc1ba Mon Sep 17 00:00:00 2001 From: Richard87 Date: Wed, 5 Jun 2024 13:10:16 +0200 Subject: [PATCH 09/11] default to connection string, use entitypath from connection string if used --- deploy/connection-string/deploy-app.yaml | 8 ++--- deploy/deploy-web.yaml | 9 ++++-- .../deploy-app-with-pod-identity.yaml | 10 +++--- .../deploy-app-with-workload-identity.yaml | 6 ++-- pod-identity.md | 2 +- src/Keda.Samples.DotNet.Web/appsettings.json | 2 +- .../AddServiceBusClientExtension.cs | 19 +++++++++--- .../appsettings.json | 2 +- .../appsettings.json | 2 +- src/README.md | 10 +++--- src/docker-compose.override.yml | 31 ++++++++++--------- workload-identity.md | 2 +- 12 files changed, 60 insertions(+), 43 deletions(-) diff --git a/deploy/connection-string/deploy-app.yaml b/deploy/connection-string/deploy-app.yaml index 2f02f23..113a230 100644 --- a/deploy/connection-string/deploy-app.yaml +++ b/deploy/connection-string/deploy-app.yaml @@ -17,14 +17,14 @@ spec: - name: order-processor image: ghcr.io/kedacore/sample-dotnet-worker-servicebus-queue:latest env: - - name: KEDA_SERVICEBUS_AUTH_MODE + - name: OrderQueueOptions__AuthMode value: ConnectionString - - name: KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING + - name: OrderQueueOptions__ConnectionString valueFrom: secretKeyRef: name: secrets-order-consumer key: servicebus-connectionstring - - name: KEDA_SERVICEBUS_QUEUE_NAME + - name: OrderQueueOptions__QueueName value: orders --- apiVersion: v1 @@ -34,4 +34,4 @@ metadata: labels: app: order-processor data: - servicebus-connectionstring: \ No newline at end of file + servicebus-connectionstring: diff --git a/deploy/deploy-web.yaml b/deploy/deploy-web.yaml index 1e5c82f..978ebb7 100644 --- a/deploy/deploy-web.yaml +++ b/deploy/deploy-web.yaml @@ -17,18 +17,23 @@ spec: - name: order-web image: ghcr.io/kedacore/sample-dotnet-worker-servicebus-portal env: - - name: KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING + - name: OrderQueueOptions__AuthMode + value: ConnectionString + env: + - name: OrderQueueOptions__ConnectionString valueFrom: secretKeyRef: name: secrets-order-portal key: servicebus-connectionstring + - name: OrderQueueOptions__QueueName + value: orders --- apiVersion: v1 kind: Service metadata: name: kedasampleweb labels: - app: order-web + app: order-web annotations: # Expose service on ..cloudapp.azure.com service.beta.kubernetes.io/azure-dns-label-name: keda-orders-portal diff --git a/deploy/pod-identity/deploy-app-with-pod-identity.yaml b/deploy/pod-identity/deploy-app-with-pod-identity.yaml index 0a44a14..2ff8fe8 100644 --- a/deploy/pod-identity/deploy-app-with-pod-identity.yaml +++ b/deploy/pod-identity/deploy-app-with-pod-identity.yaml @@ -37,11 +37,11 @@ spec: - name: order-processor image: ghcr.io/kedacore/sample-dotnet-worker-servicebus-queue:latest env: - - name: KEDA_SERVICEBUS_AUTH_MODE + - name: OrderQueueOptions__AuthMode value: PodIdentity - - name: KEDA_SERVICEBUS_HOST_NAME + - name: OrderQueueOptions__FullyQualifiedNamespace value: .servicebus.windows.net - - name: KEDA_SERVICEBUS_QUEUE_NAME + - name: OrderQueueOptions__QueueName value: orders - - name: KEDA_SERVICEBUS_IDENTITY_USERASSIGNEDID - value: \ No newline at end of file + - name: OrderQueueOptions__ClientId + value: diff --git a/deploy/workload-identity/deploy-app-with-workload-identity.yaml b/deploy/workload-identity/deploy-app-with-workload-identity.yaml index 42aefcd..3ddb9ca 100644 --- a/deploy/workload-identity/deploy-app-with-workload-identity.yaml +++ b/deploy/workload-identity/deploy-app-with-workload-identity.yaml @@ -28,9 +28,9 @@ spec: - name: order-processor image: ghcr.io/kedacore/sample-dotnet-worker-servicebus-queue:latest env: - - name: KEDA_SERVICEBUS_AUTH_MODE + - name: OrderQueueOptions__AuthMode value: WorkloadIdentity - - name: KEDA_SERVICEBUS_HOST_NAME + - name: OrderQueueOptions__FullyQualifiedNamespace value: .servicebus.windows.net - - name: KEDA_SERVICEBUS_QUEUE_NAME + - name: OrderQueueOptions__QueueName value: orders diff --git a/pod-identity.md b/pod-identity.md index 6430e55..4850e01 100644 --- a/pod-identity.md +++ b/pod-identity.md @@ -339,7 +339,7 @@ To build and run the web app locally, add the service bus connection string to a There is also a docker image available, so you can also run it locally with the following command: ```cli -docker run -p 8080:80 -d -e OrderQueue__ConnectionString="KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING" kedasamples/sample-dotnet-web +docker run -p 8080:80 -d -e OrderQueue__ConnectionString="" kedasamples/sample-dotnet-web ``` To deploy the web application to your Kubernetes cluster: diff --git a/src/Keda.Samples.DotNet.Web/appsettings.json b/src/Keda.Samples.DotNet.Web/appsettings.json index 02116dc..dcca987 100644 --- a/src/Keda.Samples.DotNet.Web/appsettings.json +++ b/src/Keda.Samples.DotNet.Web/appsettings.json @@ -9,7 +9,7 @@ "AllowedHosts": "*", "OrderQueueOptions": { "QueueName": "orders", - "AuthMode": "AzureDefaultCredential", + "AuthMode": "ConnectionString", "FullyQualifiedNamespace": ".servicebus.windows.net", "ConnectionString": "", "TenantId": "", diff --git a/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs b/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs index 176f0d4..9cbe7df 100644 --- a/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs +++ b/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs @@ -22,19 +22,19 @@ public static IServiceCollection AddOrderQueueServices(this IServiceCollection s { var client = svc.GetRequiredService(); var options = svc.GetRequiredService>(); - return client.CreateReceiver(options.Value.QueueName); + return client.CreateReceiver(options.Value.GetEntityPath()); }); services.AddScoped(svc => { var client = svc.GetRequiredService(); var options = svc.GetRequiredService>(); - return client.CreateSender(options.Value.QueueName); + return client.CreateSender(options.Value.GetEntityPath()); }); services.AddScoped(svc => { var client = svc.GetRequiredService(); var options = svc.GetRequiredService>(); - return client.CreateProcessor(options.Value.QueueName); + return client.CreateProcessor(options.Value.GetEntityPath()); }); return services; @@ -42,6 +42,9 @@ public static IServiceCollection AddOrderQueueServices(this IServiceCollection s private static ServiceBusClient AuthenticateToAzureServiceBus(OrderQueueOptions options, ILogger logger) { + Console.WriteLine(options.AuthMode); + Console.WriteLine(options.ConnectionString); + Console.WriteLine(options.QueueName); switch (options.AuthMode) { case AuthenticationMode.AzureDefaultCredential: @@ -67,11 +70,11 @@ private static ServiceBusClient AuthenticateToAzureServiceBus(OrderQueueOptions public enum AuthenticationMode { - AzureDefaultCredential, ConnectionString, ServicePrinciple, PodIdentity, WorkloadIdentity, + AzureDefaultCredential, } public class OrderQueueOptions { @@ -86,4 +89,12 @@ public class OrderQueueOptions public string TenantId { get; set;} public string ClientId { get; set;} public string ClientSecret { get; set;} + + public string GetEntityPath() + { + if (AuthMode != AuthenticationMode.ConnectionString) return QueueName; + + var sb = ServiceBusConnectionStringProperties.Parse(ConnectionString); + return sb.EntityPath; + } } diff --git a/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json b/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json index 02116dc..dcca987 100644 --- a/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json +++ b/src/Keda.Samples.Dotnet.OrderGenerator/appsettings.json @@ -9,7 +9,7 @@ "AllowedHosts": "*", "OrderQueueOptions": { "QueueName": "orders", - "AuthMode": "AzureDefaultCredential", + "AuthMode": "ConnectionString", "FullyQualifiedNamespace": ".servicebus.windows.net", "ConnectionString": "", "TenantId": "", diff --git a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json index 02116dc..dcca987 100644 --- a/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json +++ b/src/Keda.Samples.Dotnet.OrderProcessor/appsettings.json @@ -9,7 +9,7 @@ "AllowedHosts": "*", "OrderQueueOptions": { "QueueName": "orders", - "AuthMode": "AzureDefaultCredential", + "AuthMode": "ConnectionString", "FullyQualifiedNamespace": ".servicebus.windows.net", "ConnectionString": "", "TenantId": "", diff --git a/src/README.md b/src/README.md index 1e3ba0b..752d0f7 100644 --- a/src/README.md +++ b/src/README.md @@ -1,9 +1,9 @@ # .NET Order Processor This contains the sources of the sample: -- `Keda.Samples.Dotnet.Contracts` contains the message contracts +- `Keda.Samples.Dotnet.Contracts` contains the message contracts and ServiceBus DI helper - `Keda.Samples.Dotnet.OrderGenerator` is a utility that queues new orders to an Azure Service Bus Queue -- `Keda.Samples.Dotnet.OrderProcessor` is a .NET Core 3.0 worker that processes orders from an Azure Service Bus Queue -- `Keda.Samples.Dotnet.OrderWeb` is a ASP.NET Core 3.0 web app that visualizes the size of the service bus queue +- `Keda.Samples.Dotnet.OrderProcessor` is a .NET 8.0 worker that processes orders from an Azure Service Bus Queue +- `Keda.Samples.Dotnet.OrderWeb` is a ASP.NET 8.0 web app that visualizes the size of the service bus queue ## Building & Running the sample locally on Docker @@ -15,13 +15,13 @@ Build Docker container for order processor Run order processor locally ```shell -❯ docker run --detach --env KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING="" keda-sample-dotnet-worker-servicebus-queue +❯ docker run --detach --env OrderQueueOptions__ConnectionString="" keda-sample-dotnet-worker-servicebus-queue c6775c9383e56fc16da37b62ebbff0dc44d4019a53d282a1ef260a6d71022a32 ``` Let's use the test orders via our `OrderGenerator` tool: ```shell -❯ dotnet run --project .\Keda.Samples.Dotnet.OrderGenerator\Keda.Samples.Dotnet.OrderGenerator.csproj +❯ OrderQueueOptions__ConnectionString="" dotnet run --project .\Keda.Samples.Dotnet.OrderGenerator\Keda.Samples.Dotnet.OrderGenerator.csproj Let's queue some orders, how many do you want? 2 Queuing order 719a7b19-f1f7-4f46-a543-8da9bfaf843d - A Hat for Reilly Davis diff --git a/src/docker-compose.override.yml b/src/docker-compose.override.yml index 379eaa3..ed2229c 100644 --- a/src/docker-compose.override.yml +++ b/src/docker-compose.override.yml @@ -4,25 +4,26 @@ services: keda.samples.worker.servicebus: environment: - ASPNETCORE_ENVIRONMENT=Development - - KEDA_SERVICEBUS_QUEUE_NAME=orders - + - OrderQueueOptions__QueueName=orders + # Authentication mode: Connection string - - KEDA_SERVICEBUS_AUTH_MODE=ConnectionString - - KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING= - + - OrderQueueOptions__AuthMode=ConnectionString + - OrderQueueOptions__ConnectionString= + # Authentication mode: Service Principle - # - KEDA_SERVICEBUS_AUTH_MODE=ServicePrinciple - # - KEDA_SERVICEBUS_HOST_NAME=.servicebus.windows.net - # - KEDA_SERVICEBUS_TENANT_ID= - # - KEDA_SERVICEBUS_IDENTITY_APPID= - # - KEDA_SERVICEBUS_IDENTITY_SECRET= - + # - OrderQueueOptions__AuthMode=ServicePrinciple + # - OrderQueueOptions__FullyQualifiedNamespace=.servicebus.windows.net + # - OrderQueueOptions__TenantId= + # - OrderQueueOptions__ClientId= + # - OrderQueueOptions__ClientSecret= + # Authentication mode: Managed Identity - #- KEDA_SERVICEBUS_AUTH_MODE=ManagedIdentity - #- KEDA_SERVICEBUS_HOST_NAME=.servicebus.windows.net + #- OrderQueueOptions__AuthMode=ManagedIdentity + #- OrderQueueOptions__FullyQualifiedNamespace=.servicebus.windows.net # Optionally specify ID of user-assigned application identity - #- KEDA_SERVICEBUS_IDENTITY_USERASSIGNEDID= + #- OrderQueueOptions__ClientId= keda.samples.web: environment: - ASPNETCORE_ENVIRONMENT=Development - - KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING= \ No newline at end of file + - OrderQueueOptions__AuthMode=ConnectionString + - OrderQueueOptions__ConnectionString= diff --git a/workload-identity.md b/workload-identity.md index 7c9cc70..989b645 100644 --- a/workload-identity.md +++ b/workload-identity.md @@ -367,7 +367,7 @@ To build and run the web app locally, add the service bus connection string to a There is also a docker image available, so you can also run it locally with the following command: ```cli -docker run -p 8080:80 -d -e OrderQueue__ConnectionString="KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING" kedasamples/sample-dotnet-web +docker run -p 8080:80 -d -e OrderQueue__ConnectionString="" kedasamples/sample-dotnet-web ``` To deploy the web application to your Kubernetes cluster: From 2e3f9cb255f2f7c843178936ad644346935e09e0 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Wed, 5 Jun 2024 13:16:31 +0200 Subject: [PATCH 10/11] Sign commit Signed-off-by: Richard87 --- workload-identity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workload-identity.md b/workload-identity.md index 989b645..6469cec 100644 --- a/workload-identity.md +++ b/workload-identity.md @@ -413,7 +413,7 @@ Run the following command with app ids of both created Azure AD applications. ```cli ❯ az ad app delete --id ``` - + ### Uninstall KEDA ```cli From 524688a581fb7821cc592cd11abcceb06d68b443 Mon Sep 17 00:00:00 2001 From: Richard87 Date: Thu, 6 Jun 2024 10:11:24 +0200 Subject: [PATCH 11/11] split multi-class files into more files --- src/Keda.Samples.DotNet.Web/Program.cs | 2 +- .../AddServiceBusClientExtension.cs | 45 +++---------------- src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs | 5 --- src/Keda.Samples.Dotnet.Contracts/Customer.cs | 3 ++ src/Keda.Samples.Dotnet.Contracts/Order.cs | 4 ++ .../OrderQueueOptions.cs | 37 +++++++++++++++ .../QueueStatus.cs | 3 ++ 7 files changed, 53 insertions(+), 46 deletions(-) delete mode 100644 src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs create mode 100644 src/Keda.Samples.Dotnet.Contracts/Customer.cs create mode 100644 src/Keda.Samples.Dotnet.Contracts/Order.cs create mode 100644 src/Keda.Samples.Dotnet.Contracts/OrderQueueOptions.cs create mode 100644 src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs diff --git a/src/Keda.Samples.DotNet.Web/Program.cs b/src/Keda.Samples.DotNet.Web/Program.cs index 6beba38..122256c 100644 --- a/src/Keda.Samples.DotNet.Web/Program.cs +++ b/src/Keda.Samples.DotNet.Web/Program.cs @@ -24,7 +24,7 @@ var app = builder.Build(); -if (app.Environment.IsDevelopment())app.UseDeveloperExceptionPage(); +if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); else app.UseExceptionHandler("/Error"); app.UseStaticFiles(); diff --git a/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs b/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs index 9cbe7df..de9d09b 100644 --- a/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs +++ b/src/Keda.Samples.Dotnet.Contracts/AddServiceBusClientExtension.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel.DataAnnotations; using Azure.Identity; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.DependencyInjection; @@ -42,24 +41,21 @@ public static IServiceCollection AddOrderQueueServices(this IServiceCollection s private static ServiceBusClient AuthenticateToAzureServiceBus(OrderQueueOptions options, ILogger logger) { - Console.WriteLine(options.AuthMode); - Console.WriteLine(options.ConnectionString); - Console.WriteLine(options.QueueName); switch (options.AuthMode) { - case AuthenticationMode.AzureDefaultCredential: + case OrderQueueOptions.AuthenticationMode.AzureDefaultCredential: logger.LogInformation($"Authentication with Azure Default Credential"); return new ServiceBusClient(options.FullyQualifiedNamespace, new DefaultAzureCredential()); - case AuthenticationMode.ConnectionString: + case OrderQueueOptions.AuthenticationMode.ConnectionString: logger.LogInformation($"Authentication by using connection string"); return new ServiceBusClient(options.ConnectionString); - case AuthenticationMode.ServicePrinciple: + case OrderQueueOptions.AuthenticationMode.ServicePrinciple: logger.LogInformation("Authentication by using service principle {ClientId}", options.ClientId); return new ServiceBusClient(options.FullyQualifiedNamespace, new ClientSecretCredential(options.TenantId, options.ClientId, options.ClientSecret)); - case AuthenticationMode.PodIdentity: + case OrderQueueOptions.AuthenticationMode.PodIdentity: logger.LogInformation("Authentication by using pod identity {ClientId}", options.ClientId); return new ServiceBusClient(options.FullyQualifiedNamespace, new ManagedIdentityCredential(options.ClientId)); - case AuthenticationMode.WorkloadIdentity: + case OrderQueueOptions.AuthenticationMode.WorkloadIdentity: logger.LogInformation("Authentication by using workload identity {ClientId}", options.ClientId); return new ServiceBusClient(options.FullyQualifiedNamespace, new ManagedIdentityCredential(options.ClientId)); default: @@ -67,34 +63,3 @@ private static ServiceBusClient AuthenticateToAzureServiceBus(OrderQueueOptions } } } - -public enum AuthenticationMode -{ - ConnectionString, - ServicePrinciple, - PodIdentity, - WorkloadIdentity, - AzureDefaultCredential, -} -public class OrderQueueOptions -{ - [Required] - public AuthenticationMode AuthMode { get; set; } - [Required] - public string QueueName { get; set; } - - public string ConnectionString { get; set; } - [Required] - public string FullyQualifiedNamespace { get; set;} - public string TenantId { get; set;} - public string ClientId { get; set;} - public string ClientSecret { get; set;} - - public string GetEntityPath() - { - if (AuthMode != AuthenticationMode.ConnectionString) return QueueName; - - var sb = ServiceBusConnectionStringProperties.Parse(ConnectionString); - return sb.EntityPath; - } -} diff --git a/src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs b/src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs deleted file mode 100644 index 295d8f6..0000000 --- a/src/Keda.Samples.Dotnet.Contracts/ApiTypes.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Keda.Samples.Dotnet.Contracts; - -public record Customer(string FirstName, string LastName); -public record Order(string Id, int Amount, string ArticleNumber, Customer Customer); -public record QueueStatus(long MessageCount); \ No newline at end of file diff --git a/src/Keda.Samples.Dotnet.Contracts/Customer.cs b/src/Keda.Samples.Dotnet.Contracts/Customer.cs new file mode 100644 index 0000000..ac543db --- /dev/null +++ b/src/Keda.Samples.Dotnet.Contracts/Customer.cs @@ -0,0 +1,3 @@ +namespace Keda.Samples.Dotnet.Contracts; + +public record Customer(string FirstName, string LastName); diff --git a/src/Keda.Samples.Dotnet.Contracts/Order.cs b/src/Keda.Samples.Dotnet.Contracts/Order.cs new file mode 100644 index 0000000..0d98a54 --- /dev/null +++ b/src/Keda.Samples.Dotnet.Contracts/Order.cs @@ -0,0 +1,4 @@ +namespace Keda.Samples.Dotnet.Contracts; + + +public record Order(string Id, int Amount, string ArticleNumber, Customer Customer); diff --git a/src/Keda.Samples.Dotnet.Contracts/OrderQueueOptions.cs b/src/Keda.Samples.Dotnet.Contracts/OrderQueueOptions.cs new file mode 100644 index 0000000..e3c6684 --- /dev/null +++ b/src/Keda.Samples.Dotnet.Contracts/OrderQueueOptions.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; +using Azure.Messaging.ServiceBus; + +namespace Keda.Samples.Dotnet.Contracts; + +public class OrderQueueOptions +{ + + public enum AuthenticationMode + { + ConnectionString, + ServicePrinciple, + PodIdentity, + WorkloadIdentity, + AzureDefaultCredential, + } + + [Required] + public AuthenticationMode AuthMode { get; set; } + [Required] + public string QueueName { get; set; } + + public string ConnectionString { get; set; } + [Required] + public string FullyQualifiedNamespace { get; set;} + public string TenantId { get; set;} + public string ClientId { get; set;} + public string ClientSecret { get; set;} + + public string GetEntityPath() + { + if (AuthMode != AuthenticationMode.ConnectionString) return QueueName; + + var sb = ServiceBusConnectionStringProperties.Parse(ConnectionString); + return sb.EntityPath; + } +} diff --git a/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs b/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs new file mode 100644 index 0000000..1d78639 --- /dev/null +++ b/src/Keda.Samples.Dotnet.Contracts/QueueStatus.cs @@ -0,0 +1,3 @@ +namespace Keda.Samples.Dotnet.Contracts; + +public record QueueStatus(long MessageCount);