diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine.Tests/MigrateQueue/QueueMigrateToQuorumTests.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine.Tests/MigrateQueue/QueueMigrateToQuorumTests.cs index 6e81f46e0..d9e291c92 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine.Tests/MigrateQueue/QueueMigrateToQuorumTests.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine.Tests/MigrateQueue/QueueMigrateToQuorumTests.cs @@ -2,7 +2,7 @@ { using System; using System.Collections.Generic; - using System.CommandLine.IO; + using System.IO; using System.Threading; using System.Threading.Tasks; using global::RabbitMQ.Client; @@ -308,7 +308,9 @@ public async Task TearDown() Task ExecuteMigration(string endpointName) { - var migrationCommand = new QueueMigrateCommand(endpointName, brokerConnection, new TestConsole()); + var output = new StringWriter(); + + var migrationCommand = new QueueMigrateCommand(endpointName, brokerConnection, output); return migrationCommand.Run(); } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Bind.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Bind.cs deleted file mode 100644 index 74a448b6e..000000000 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Bind.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace NServiceBus.Transport.RabbitMQ.CommandLine -{ - using System.CommandLine.Binding; - using Microsoft.Extensions.DependencyInjection; - - static class Bind - { - public static ServiceProviderBinder FromServiceProvider() => ServiceProviderBinder.Instance; - - internal class ServiceProviderBinder : BinderBase - { - public static ServiceProviderBinder Instance { get; } = new(); - - protected override T GetBoundValue(BindingContext bindingContext) => (T)bindingContext.GetRequiredService(typeof(T)); - } - } -} diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/BrokerConnectionBinder.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/BrokerConnectionBinder.cs index 6a6de1562..4beaa5d2c 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/BrokerConnectionBinder.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/BrokerConnectionBinder.cs @@ -1,24 +1,23 @@ namespace NServiceBus.Transport.RabbitMQ.CommandLine { using System.CommandLine; - using System.CommandLine.Binding; using System.Security.Cryptography.X509Certificates; using NServiceBus.Transport.RabbitMQ.ManagementApi; class BrokerConnectionBinder(Option connectionStringOption, Option connectionStringEnvOption, Option managementApiUrlOption, Option managementApiUserNameOption, Option managementApiPasswordOption, Option certPathOption, - Option certPassphraseOption, Option disableCertificateValidationOption, Option useExternalAuthOption) : BinderBase + Option certPassphraseOption, Option disableCertificateValidationOption, Option useExternalAuthOption) { - protected override BrokerConnection GetBoundValue(BindingContext bindingContext) + public BrokerConnection CreateBrokerConnection(ParseResult parseResult) { - var connectionStringOptionValue = bindingContext.ParseResult.GetValueForOption(connectionStringOption); - var connectionStringEnvOptionValue = bindingContext.ParseResult.GetValueForOption(connectionStringEnvOption); - var managementApiUrl = bindingContext.ParseResult.GetValueForOption(managementApiUrlOption); - var managementApiUserName = bindingContext.ParseResult.GetValueForOption(managementApiUserNameOption); - var managementApiPassword = bindingContext.ParseResult.GetValueForOption(managementApiPasswordOption); - var certPath = bindingContext.ParseResult.GetValueForOption(certPathOption); - var certPassphrase = bindingContext.ParseResult.GetValueForOption(certPassphraseOption); - var disableCertificateValidation = bindingContext.ParseResult.GetValueForOption(disableCertificateValidationOption); - var useExternalAuth = bindingContext.ParseResult.GetValueForOption(useExternalAuthOption); + var connectionStringOptionValue = parseResult.GetValue(connectionStringOption); + var connectionStringEnvOptionValue = parseResult.GetValue(connectionStringEnvOption); + var managementApiUrl = parseResult.GetValue(managementApiUrlOption); + var managementApiUserName = parseResult.GetValue(managementApiUserNameOption); + var managementApiPassword = parseResult.GetValue(managementApiPasswordOption); + var certPath = parseResult.GetValue(certPathOption); + var certPassphrase = parseResult.GetValue(certPassphraseOption); + var disableCertificateValidation = parseResult.GetValue(disableCertificateValidationOption); + var useExternalAuth = parseResult.GetValue(useExternalAuthOption); var connectionString = GetConnectionString(connectionStringOptionValue, connectionStringEnvOptionValue); diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/BrokerVerifierBinder.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/BrokerVerifierBinder.cs index 080c30b57..f5975da74 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/BrokerVerifierBinder.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/BrokerVerifierBinder.cs @@ -1,19 +1,18 @@ namespace NServiceBus.Transport.RabbitMQ.CommandLine { using System.CommandLine; - using System.CommandLine.Binding; using NServiceBus.Transport.RabbitMQ.ManagementApi; - class BrokerVerifierBinder(Option connectionStringOption, Option connectionStringEnvOption, Option managementApiUrlOption, Option managementApiUserNameOption, Option managementApiPasswordOption, Option disableCertificateValidationOption) : BinderBase + class BrokerVerifierBinder(Option connectionStringOption, Option connectionStringEnvOption, Option managementApiUrlOption, Option managementApiUserNameOption, Option managementApiPasswordOption, Option disableCertificateValidationOption) { - protected override BrokerVerifier GetBoundValue(BindingContext bindingContext) + public BrokerVerifier CreateBrokerVerifier(ParseResult parseResult) { - var connectionStringOptionValue = bindingContext.ParseResult.GetValueForOption(connectionStringOption); - var connectionStringEnvOptionValue = bindingContext.ParseResult.GetValueForOption(connectionStringEnvOption); - var managementApiUrl = bindingContext.ParseResult.GetValueForOption(managementApiUrlOption); - var managementApiUserName = bindingContext.ParseResult.GetValueForOption(managementApiUserNameOption); - var managementApiPassword = bindingContext.ParseResult.GetValueForOption(managementApiPasswordOption); - var disableCertificateValidation = bindingContext.ParseResult.GetValueForOption(disableCertificateValidationOption); + var connectionStringOptionValue = parseResult.GetValue(connectionStringOption); + var connectionStringEnvOptionValue = parseResult.GetValue(connectionStringEnvOption); + var managementApiUrl = parseResult.GetValue(managementApiUrlOption); + var managementApiUserName = parseResult.GetValue(managementApiUserNameOption); + var managementApiPassword = parseResult.GetValue(managementApiPasswordOption); + var disableCertificateValidation = parseResult.GetValue(disableCertificateValidationOption); var connectionString = GetConnectionString(connectionStringOptionValue, connectionStringEnvOptionValue); diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysCreateCommand.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysCreateCommand.cs index 22a8106a5..64f65b4f3 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysCreateCommand.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysCreateCommand.cs @@ -1,8 +1,9 @@ namespace NServiceBus.Transport.RabbitMQ.CommandLine { using System.CommandLine; + using System.Threading; - class DelaysCreateCommand + class DelaysCreateCommand(BrokerConnection brokerConnection, TextWriter output) { public static Command CreateCommand() { @@ -10,35 +11,27 @@ public static Command CreateCommand() var brokerConnectionBinder = SharedOptions.CreateBrokerConnectionBinderWithOptions(command); - command.SetHandler(async (brokerConnection, console, cancellationToken) => + command.SetAction(async (parseResult, cancellationToken) => { - var delaysCreate = new DelaysCreateCommand(brokerConnection, console); + var brokerConnection = brokerConnectionBinder.CreateBrokerConnection(parseResult); + + var delaysCreate = new DelaysCreateCommand(brokerConnection, parseResult.Configuration.Output); await delaysCreate.Run(cancellationToken); - }, - brokerConnectionBinder, Bind.FromServiceProvider(), Bind.FromServiceProvider()); + }); return command; } - public DelaysCreateCommand(BrokerConnection brokerConnection, IConsole console) - { - this.brokerConnection = brokerConnection; - this.console = console; - } - public async Task Run(CancellationToken cancellationToken = default) { - console.WriteLine($"Creating v2 delay infrastructure queues and exchanges..."); + output.WriteLine($"Creating v2 delay infrastructure queues and exchanges..."); using var connection = await brokerConnection.Create(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); await DelayInfrastructure.Build(channel, cancellationToken); - console.WriteLine("Queues and exchanges created successfully"); + output.WriteLine("Queues and exchanges created successfully"); } - - readonly BrokerConnection brokerConnection; - readonly IConsole console; } } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysMigrateCommand.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysMigrateCommand.cs index 13648a95b..757ba4ae1 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysMigrateCommand.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysMigrateCommand.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using global::RabbitMQ.Client; - class DelaysMigrateCommand(BrokerConnection brokerConnection, IRoutingTopology routingTopology, IConsole console) + class DelaysMigrateCommand(BrokerConnection brokerConnection, IRoutingTopology routingTopology, TextWriter output) { const string poisonMessageQueue = "delays-migrate-poison-messages"; const string timeSentHeader = "NServiceBus.TimeSent"; @@ -21,10 +21,13 @@ public static Command CreateCommand() var brokerConnectionBinder = SharedOptions.CreateBrokerConnectionBinderWithOptions(command); var routingTopologyTypeOption = SharedOptions.CreateRoutingTopologyTypeOption(); - command.AddOption(routingTopologyTypeOption); + command.Options.Add(routingTopologyTypeOption); - command.SetHandler(async (brokerConnection, routingTopologyType, console, cancellationToken) => + command.SetAction(async (parseResult, cancellationToken) => { + var brokerConnection = brokerConnectionBinder.CreateBrokerConnection(parseResult); + var routingTopologyType = parseResult.GetValue(routingTopologyTypeOption); + IRoutingTopology routingTopology = routingTopologyType switch { RoutingTopologyType.Conventional => new ConventionalRoutingTopology(true, QueueType.Quorum), @@ -32,10 +35,9 @@ public static Command CreateCommand() _ => throw new InvalidOperationException() }; - var delaysMigrate = new DelaysMigrateCommand(brokerConnection, routingTopology, console); + var delaysMigrate = new DelaysMigrateCommand(brokerConnection, routingTopology, parseResult.Configuration.Output); await delaysMigrate.Run(cancellationToken); - }, - brokerConnectionBinder, routingTopologyTypeOption, Bind.FromServiceProvider(), Bind.FromServiceProvider()); + }); return command; } @@ -60,7 +62,7 @@ async Task MigrateQueue(IChannel channel, int delayLevel, CancellationToken canc if (messageCount > 0) { - console.Write($"Processing {messageCount} messages at delay level {delayLevel:00}. "); + output.Write($"Processing {messageCount} messages at delay level {delayLevel:00}. "); int skippedMessages = 0; int processedMessages = 0; @@ -132,11 +134,11 @@ async Task MigrateQueue(IChannel channel, int delayLevel, CancellationToken canc processedMessages++; } - console.WriteLine($"{processedMessages} successful, {skippedMessages} skipped."); + output.WriteLine($"{processedMessages} successful, {skippedMessages} skipped."); } else { - console.WriteLine($"No messages to process at delay level {delayLevel:00}."); + output.WriteLine($"No messages to process at delay level {delayLevel:00}."); } } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysVerifyCommand.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysVerifyCommand.cs index ed9764648..4eee2d558 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysVerifyCommand.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Delays/DelaysVerifyCommand.cs @@ -3,7 +3,7 @@ using System; using System.CommandLine; - class DelaysVerifyCommand(BrokerVerifier brokerVerifier, IConsole console) + class DelaysVerifyCommand(BrokerVerifier brokerVerifier, TextWriter output) { public static Command CreateCommand() { @@ -11,12 +11,13 @@ public static Command CreateCommand() var brokerVerifierBinder = SharedOptions.CreateBrokerVerifierBinderWithOptions(command); - command.SetHandler(async (brokerVerifier, console, cancellationToken) => + command.SetAction(async (parseResult, cancellationToken) => { - var delaysVerify = new DelaysVerifyCommand(brokerVerifier, console); + var brokerVerifier = brokerVerifierBinder.CreateBrokerVerifier(parseResult); + + var delaysVerify = new DelaysVerifyCommand(brokerVerifier, parseResult.Configuration.Output); await delaysVerify.Run(cancellationToken); - }, - brokerVerifierBinder, Bind.FromServiceProvider(), Bind.FromServiceProvider()); + }); return command; } @@ -28,11 +29,11 @@ public async Task Run(CancellationToken cancellationToken = default) await brokerVerifier.Initialize(cancellationToken); await brokerVerifier.VerifyRequirements(cancellationToken); - console.WriteLine("All checks OK"); + output.WriteLine("All checks OK"); } catch (Exception ex) when (!ex.IsCausedBy(cancellationToken)) { - console.WriteLine($"Fail: {ex.Message}"); + output.WriteLine($"Fail: {ex.Message}"); } } } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Endpoint/EndpointCreateCommand.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Endpoint/EndpointCreateCommand.cs index c3deb0eba..020aa87fd 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Endpoint/EndpointCreateCommand.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Endpoint/EndpointCreateCommand.cs @@ -3,69 +3,62 @@ using System.CommandLine; using global::RabbitMQ.Client.Exceptions; - class EndpointCreateCommand + class EndpointCreateCommand(BrokerConnection brokerConnection, IRoutingTopology routingTopology, TextWriter output, TextWriter error) { - readonly BrokerConnection brokerConnection; - readonly IRoutingTopology routingTopology; - readonly IConsole console; - public static Command CreateCommand() { var command = new Command("create", "Create queues and exchanges for an endpoint"); - var endpointNameArgument = new Argument( - name: "endpointName", - description: "The name of the endpoint to create"); + var endpointNameArgument = new Argument("endpointName") + { + Description = "The name of the endpoint to create" + }; - var errorQueueOption = new Option( - name: "--errorQueueName", - description: "Also create an error queue with the specified name"); + var errorQueueOption = new Option("--errorQueueName") + { + Description = "Also create an error queue with the specified name" + }; - var auditQueueOption = new Option( - name: "--auditQueueName", - description: "Also create an audit queue with the specified name"); + var auditQueueOption = new Option("--auditQueueName") + { + Description = "Also create an audit queue with the specified name" + }; - var instanceDiscriminatorsOption = new Option>( - name: "--instanceDiscriminators", - description: "An optional list of instance discriminators to use when the endpoint needs uniquely addressable instances") + var instanceDiscriminatorsOption = new Option>("--instanceDiscriminators") { Arity = ArgumentArity.ZeroOrMore, - AllowMultipleArgumentsPerToken = true + AllowMultipleArgumentsPerToken = true, + Description = "An optional list of instance discriminators to use when the endpoint needs uniquely addressable instances", }; var brokerConnectionBinder = SharedOptions.CreateBrokerConnectionBinderWithOptions(command); var routingTopologyBinder = SharedOptions.CreateRoutingTopologyBinderWithOptions(command); - command.AddArgument(endpointNameArgument); - command.AddOption(errorQueueOption); - command.AddOption(auditQueueOption); - command.AddOption(instanceDiscriminatorsOption); + command.Arguments.Add(endpointNameArgument); + command.Options.Add(errorQueueOption); + command.Options.Add(auditQueueOption); + command.Options.Add(instanceDiscriminatorsOption); - command.SetHandler(async (endpointName, errorQueue, auditQueue, instanceDiscriminators, brokerConnection, routingTopology, console, cancellationToken) => + command.SetAction(async (parseResult, cancellationToken) => { - var queueCreate = new EndpointCreateCommand(brokerConnection, routingTopology, console); - await queueCreate.Run(endpointName, errorQueue, auditQueue, instanceDiscriminators, cancellationToken); - }, - endpointNameArgument, errorQueueOption, auditQueueOption, instanceDiscriminatorsOption, brokerConnectionBinder, routingTopologyBinder, Bind.FromServiceProvider(), Bind.FromServiceProvider()); + var brokerConnection = brokerConnectionBinder.CreateBrokerConnection(parseResult); + var routingTopology = routingTopologyBinder.CreateRoutingTopology(parseResult); - return command; - } + var queueCreate = new EndpointCreateCommand(brokerConnection, routingTopology, parseResult.Configuration.Output, parseResult.Configuration.Error); + await queueCreate.Run(parseResult.GetRequiredValue(endpointNameArgument), parseResult.GetValue(errorQueueOption), parseResult.GetValue(auditQueueOption), parseResult.GetValue(instanceDiscriminatorsOption), cancellationToken); + }); - public EndpointCreateCommand(BrokerConnection brokerConnection, IRoutingTopology routingTopology, IConsole console) - { - this.brokerConnection = brokerConnection; - this.routingTopology = routingTopology; - this.console = console; + return command; } - public async Task Run(string endpointName, string? errorQueue, string? auditQueue, IEnumerable instanceDiscriminators, CancellationToken cancellationToken = default) + public async Task Run(string endpointName, string? errorQueue, string? auditQueue, IEnumerable? instanceDiscriminators, CancellationToken cancellationToken = default) { - console.WriteLine("Connecting to broker"); + output.WriteLine("Connecting to broker"); using var connection = await brokerConnection.Create(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - console.WriteLine("Checking for v2 delay infrastructure"); + output.WriteLine("Checking for v2 delay infrastructure"); try { @@ -73,15 +66,15 @@ public async Task Run(string endpointName, string? errorQueue, string? auditQueu } catch (OperationInterruptedException) { - console.Error.Write("Fail: v2 delay infrastructure not found.\n"); + error.Write("Fail: v2 delay infrastructure not found.\n"); throw; } - console.WriteLine($"Creating queues"); + output.WriteLine($"Creating queues"); var receivingAddresses = new List() { endpointName }; - if (instanceDiscriminators.Any()) + if (instanceDiscriminators is not null && instanceDiscriminators.Any()) { receivingAddresses.AddRange(instanceDiscriminators.Select(discriminator => $"{endpointName}-{discriminator}")); } @@ -105,7 +98,7 @@ public async Task Run(string endpointName, string? errorQueue, string? auditQueu await routingTopology.BindToDelayInfrastructure(channel, receivingAddress, DelayInfrastructure.DeliveryExchange, DelayInfrastructure.BindingKey(receivingAddress), cancellationToken); } - console.WriteLine($"Completed successfully"); + output.WriteLine($"Completed successfully"); } } } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Queue/QueueMigrateCommand.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Queue/QueueMigrateCommand.cs index 541897f24..4baadb1fa 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Queue/QueueMigrateCommand.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Queue/QueueMigrateCommand.cs @@ -6,35 +6,35 @@ using global::RabbitMQ.Client; using global::RabbitMQ.Client.Exceptions; - class QueueMigrateCommand(string queueName, BrokerConnection brokerConnection, IConsole console) + class QueueMigrateCommand(string queueName, BrokerConnection brokerConnection, TextWriter output) { public static Command CreateCommand() { var command = new Command("migrate-to-quorum", "Migrate an existing classic queue to a quorum queue"); - var queueNameArgument = new Argument() + var queueNameArgument = new Argument("queueName") { - Name = "queueName", Description = "The name of the classic queue to migrate to a quorum queue" }; var brokerConnectionBinder = SharedOptions.CreateBrokerConnectionBinderWithOptions(command); - command.AddArgument(queueNameArgument); + command.Arguments.Add(queueNameArgument); - command.SetHandler(async (queueName, brokerConnection, console, cancellationToken) => + command.SetAction(async (parseResult, cancellationToken) => { - var migrateCommand = new QueueMigrateCommand(queueName, brokerConnection, console); + var brokerConnection = brokerConnectionBinder.CreateBrokerConnection(parseResult); + + var migrateCommand = new QueueMigrateCommand(parseResult.GetRequiredValue(queueNameArgument), brokerConnection, parseResult.Configuration.Output); await migrateCommand.Run(cancellationToken); - }, - queueNameArgument, brokerConnectionBinder, Bind.FromServiceProvider(), Bind.FromServiceProvider()); + }); return command; } public async Task Run(CancellationToken cancellationToken = default) { - console.WriteLine($"Starting migration of '{queueName}'"); + output.WriteLine($"Starting migration of '{queueName}'"); await using var connection = await brokerConnection.Create(cancellationToken); @@ -71,20 +71,20 @@ async Task MoveMessagesToHoldingQueue(IConnection connection, Ca var createChannelOptions = new CreateChannelOptions(publisherConfirmationsEnabled: true, publisherConfirmationTrackingEnabled: true, outstandingPublisherConfirmationsRateLimiter: null); await using var channel = await connection.CreateChannelAsync(createChannelOptions, cancellationToken); - console.WriteLine($"Migrating messages from '{queueName}' to '{holdingQueueName}'"); + output.WriteLine($"Migrating messages from '{queueName}' to '{holdingQueueName}'"); // does the holding queue need to be quorum? await channel.QueueDeclareAsync(holdingQueueName, true, false, false, quorumQueueArguments, cancellationToken: cancellationToken); - console.WriteLine($"Created queue '{holdingQueueName}'"); + output.WriteLine($"Created queue '{holdingQueueName}'"); // bind the holding queue to the exchange of the queue under migration // this will throw if the exchange for the queue doesn't exist await channel.QueueBindAsync(holdingQueueName, queueName, string.Empty, cancellationToken: cancellationToken); - console.WriteLine($"Bound '{holdingQueueName}' to exchange '{queueName}'"); + output.WriteLine($"Bound '{holdingQueueName}' to exchange '{queueName}'"); // unbind the queue under migration to stop more messages from coming in await channel.QueueUnbindAsync(queueName, queueName, string.Empty, cancellationToken: cancellationToken); - console.WriteLine($"Unbound '{queueName}' from exchange '{queueName}' "); + output.WriteLine($"Unbound '{queueName}' from exchange '{queueName}' "); // move all existing messages to the holding queue var numMessagesMovedToHolding = await ProcessMessages( @@ -96,7 +96,7 @@ async Task MoveMessagesToHoldingQueue(IConnection connection, Ca }, cancellationToken); - console.WriteLine($"Moved {numMessagesMovedToHolding} messages to '{holdingQueueName}'"); + output.WriteLine($"Moved {numMessagesMovedToHolding} messages to '{holdingQueueName}'"); return MigrationStage.MessagesMovedToHoldingQueue; } @@ -112,7 +112,7 @@ async Task DeleteMainQueue(IConnection connection, CancellationT // delete the queue under migration await channel.QueueDeleteAsync(queueName, cancellationToken: cancellationToken); - console.WriteLine($"Removed '{queueName}'"); + output.WriteLine($"Removed '{queueName}'"); return MigrationStage.ClassicQueueDeleted; } @@ -122,7 +122,7 @@ async Task CreateMainQueueAsQuorum(IConnection connection, Cance await using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); await channel.QueueDeclareAsync(queueName, true, false, false, quorumQueueArguments, cancellationToken: cancellationToken); - console.WriteLine($"Recreated '{queueName}' as a quorum queue"); + output.WriteLine($"Recreated '{queueName}' as a quorum queue"); return MigrationStage.QuorumQueueCreated; } @@ -133,10 +133,10 @@ async Task RestoreMessages(IConnection connection, CancellationT await using var channel = await connection.CreateChannelAsync(createChannelOptions, cancellationToken); await channel.QueueBindAsync(queueName, queueName, string.Empty, cancellationToken: cancellationToken); - console.WriteLine($"Re-bound '{queueName}' to exchange '{queueName}'"); + output.WriteLine($"Re-bound '{queueName}' to exchange '{queueName}'"); await channel.QueueUnbindAsync(holdingQueueName, queueName, string.Empty, cancellationToken: cancellationToken); - console.WriteLine($"Unbound '{holdingQueueName}' from exchange '{queueName}'"); + output.WriteLine($"Unbound '{holdingQueueName}' from exchange '{queueName}'"); var messageIds = new Dictionary(); @@ -170,7 +170,7 @@ async Task RestoreMessages(IConnection connection, CancellationT }, cancellationToken); - console.WriteLine($"Moved {numMessageMovedBackToMain} messages from '{holdingQueueName}' to '{queueName}'"); + output.WriteLine($"Moved {numMessageMovedBackToMain} messages from '{holdingQueueName}' to '{queueName}'"); return MigrationStage.MessagesMovedToQuorumQueue; } @@ -185,7 +185,7 @@ async Task CleanUpHoldingQueue(IConnection connection, Cancellat } await channel.QueueDeleteAsync(holdingQueueName, cancellationToken: cancellationToken); - console.WriteLine($"Removed '{holdingQueueName}'"); + output.WriteLine($"Removed '{holdingQueueName}'"); return MigrationStage.CleanUpCompleted; } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Queue/QueueValidateDeliveryLimitCommand.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Queue/QueueValidateDeliveryLimitCommand.cs index 987c73ff5..bb9bcb46c 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Queue/QueueValidateDeliveryLimitCommand.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Commands/Queue/QueueValidateDeliveryLimitCommand.cs @@ -2,28 +2,28 @@ { using System.CommandLine; - class QueueValidateDeliveryLimitCommand(string queueName, BrokerVerifier brokerVerifier, IConsole console) + class QueueValidateDeliveryLimitCommand(string queueName, BrokerVerifier brokerVerifier, TextWriter output) { public static Command CreateCommand() { var command = new Command("validate-delivery-limit", "Validate that the queue has an unlimited delivery limit"); - var queueNameArgument = new Argument() + var queueNameArgument = new Argument("queueName") { - Name = "queueName", Description = "The name of the queue to validate" }; var brokerVerifierBinder = SharedOptions.CreateBrokerVerifierBinderWithOptions(command); - command.AddArgument(queueNameArgument); + command.Arguments.Add(queueNameArgument); - command.SetHandler(async (queueName, brokerVerifier, console, cancellationToken) => + command.SetAction(async (parseResult, cancellationToken) => { - var validateCommand = new QueueValidateDeliveryLimitCommand(queueName, brokerVerifier, console); + var brokerVerifier = brokerVerifierBinder.CreateBrokerVerifier(parseResult); + + var validateCommand = new QueueValidateDeliveryLimitCommand(parseResult.GetRequiredValue(queueNameArgument), brokerVerifier, parseResult.Configuration.Output); await validateCommand.Run(cancellationToken); - }, - queueNameArgument, brokerVerifierBinder, Bind.FromServiceProvider(), Bind.FromServiceProvider()); + }); return command; } @@ -37,7 +37,7 @@ public async Task Run(CancellationToken cancellationToken = default) } catch (Exception ex) when (!ex.IsCausedBy(cancellationToken)) { - console.WriteLine(ex.Message); + output.WriteLine(ex.Message); } } } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/NServiceBus.Transport.RabbitMQ.CommandLine.csproj b/src/NServiceBus.Transport.RabbitMQ.CommandLine/NServiceBus.Transport.RabbitMQ.CommandLine.csproj index e721e3090..8193f1c20 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/NServiceBus.Transport.RabbitMQ.CommandLine.csproj +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/NServiceBus.Transport.RabbitMQ.CommandLine.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -19,7 +19,7 @@ - + diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Program.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Program.cs index c6197eeae..035209fcf 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/Program.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/Program.cs @@ -7,34 +7,36 @@ CreateEndpointCommand(rootCommand); CreateQueueCommand(rootCommand); -return await rootCommand.InvokeAsync(args); +var parseResult = rootCommand.Parse(args); + +return await parseResult.InvokeAsync(); void CreateDelaysCommand(Command rootCommand) { var delaysSubCommand = new Command("delays", "Commands to manage the queues and exchanges for the NServiceBus delay infrastructure"); - delaysSubCommand.AddCommand(DelaysCreateCommand.CreateCommand()); - delaysSubCommand.AddCommand(DelaysMigrateCommand.CreateCommand()); - delaysSubCommand.AddCommand(DelaysVerifyCommand.CreateCommand()); + delaysSubCommand.Subcommands.Add(DelaysCreateCommand.CreateCommand()); + delaysSubCommand.Subcommands.Add(DelaysMigrateCommand.CreateCommand()); + delaysSubCommand.Subcommands.Add(DelaysVerifyCommand.CreateCommand()); - rootCommand.AddCommand(delaysSubCommand); + rootCommand.Subcommands.Add(delaysSubCommand); } void CreateEndpointCommand(Command rootCommand) { var endpointSubCommand = new Command("endpoint", "Commands to manage the infrastructure for an NServiceBus endpoint"); - endpointSubCommand.AddCommand(EndpointCreateCommand.CreateCommand()); + endpointSubCommand.Subcommands.Add(EndpointCreateCommand.CreateCommand()); - rootCommand.AddCommand(endpointSubCommand); + rootCommand.Subcommands.Add(endpointSubCommand); } void CreateQueueCommand(Command rootCommand) { var queueSubCommand = new Command("queue", "Commands to manage individual queues"); - queueSubCommand.AddCommand(QueueMigrateCommand.CreateCommand()); - queueSubCommand.AddCommand(QueueValidateDeliveryLimitCommand.CreateCommand()); + queueSubCommand.Subcommands.Add(QueueMigrateCommand.CreateCommand()); + queueSubCommand.Subcommands.Add(QueueValidateDeliveryLimitCommand.CreateCommand()); - rootCommand.AddCommand(queueSubCommand); + rootCommand.Subcommands.Add(queueSubCommand); } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/RoutingTopologyBinder.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/RoutingTopologyBinder.cs index 9646d6cd1..3e5a98bd5 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/RoutingTopologyBinder.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/RoutingTopologyBinder.cs @@ -1,22 +1,14 @@ namespace NServiceBus.Transport.RabbitMQ.CommandLine { using System.CommandLine; - using System.CommandLine.Binding; - class RoutingTopologyBinder : BinderBase + class RoutingTopologyBinder(Option routingTopologyTypeOption, Option useDurableEntitiesOption, Option queueTypeOption) { - public RoutingTopologyBinder(Option routingTopologyTypeOption, Option useDurableEntitiesOption, Option queueTypeOption) + public IRoutingTopology CreateRoutingTopology(ParseResult parseResult) { - this.routingTopologyTypeOption = routingTopologyTypeOption; - this.useDurableEntitiesOption = useDurableEntitiesOption; - this.queueTypeOption = queueTypeOption; - } - - protected override IRoutingTopology GetBoundValue(BindingContext bindingContext) - { - var routingTopologyType = bindingContext.ParseResult.GetValueForOption(routingTopologyTypeOption); - var useDurableEntities = bindingContext.ParseResult.GetValueForOption(useDurableEntitiesOption); - var queueType = bindingContext.ParseResult.GetValueForOption(queueTypeOption); + var routingTopologyType = parseResult.GetValue(routingTopologyTypeOption); + var useDurableEntities = parseResult.GetValue(useDurableEntitiesOption); + var queueType = parseResult.GetValue(queueTypeOption); return routingTopologyType switch { @@ -25,9 +17,5 @@ protected override IRoutingTopology GetBoundValue(BindingContext bindingContext) _ => throw new InvalidOperationException() }; } - - readonly Option routingTopologyTypeOption; - readonly Option useDurableEntitiesOption; - readonly Option queueTypeOption; } } diff --git a/src/NServiceBus.Transport.RabbitMQ.CommandLine/SharedOptions.cs b/src/NServiceBus.Transport.RabbitMQ.CommandLine/SharedOptions.cs index 45229baaa..64a450d54 100644 --- a/src/NServiceBus.Transport.RabbitMQ.CommandLine/SharedOptions.cs +++ b/src/NServiceBus.Transport.RabbitMQ.CommandLine/SharedOptions.cs @@ -8,122 +8,126 @@ static class SharedOptions public static Option CreateConnectionStringOption() { - var connectionStringOption = new Option( - name: "--connectionString", - description: $"Force this command to use the specified connection string"); - - connectionStringOption.AddAlias("-c"); + var connectionStringOption = new Option("--connectionString", "-c") + { + Description = "Force this command to use the specified connection string" + }; return connectionStringOption; } public static Option CreateConnectionStringEnvOption() { - var connectionStringEnvOption = new Option( - name: "--connectionStringEnv", - description: $"Specifies the environment variable where the connection string can be found. --connectionString, if specified, will take precedence over this option.", - getDefaultValue: () => ConnectionStringEnvironmentVariable); + var connectionStringEnvOption = new Option("--connectionStringEnv") + { + Description = "Specifies the environment variable where the connection string can be found. --connectionString, if specified, will take precedence over this option.", + DefaultValueFactory = _ => ConnectionStringEnvironmentVariable + }; return connectionStringEnvOption; } public static Option CreateManagementApiUrlOption() { - var managementApiUrlOption = new Option( - name: "--managementApiUrl", - description: $"Overrides the value inferred from the connection string"); + var managementApiUrlOption = new Option("--managementApiUrl") + { + Description = "Overrides the value inferred from the connection string" + }; return managementApiUrlOption; } public static Option CreateManagementApiUserNameOption() { - var managementApiUserNameOption = new Option( - name: "--managementApiUserName", - description: $"Overrides the value inferred from the connection string. If provided, --managementApiPassword must also be provided or this option will be ignored."); + var managementApiUserNameOption = new Option("--managementApiUserName") + { + Description = "Overrides the value inferred from the connection string. If provided, --managementApiPassword must also be provided or this option will be ignored." + }; return managementApiUserNameOption; } public static Option CreateManagementApiPasswordOption() { - var managementApiPasswordOption = new Option( - name: "--managementApiPassword", - description: $"Overrides the value inferred from the connection string. If provided, --managementApiUserName must also be provided or this option will be ignored."); + var managementApiPasswordOption = new Option("--managementApiPassword") + { + Description = "Overrides the value inferred from the connection string. If provided, --managementApiUserName must also be provided or this option will be ignored." + }; return managementApiPasswordOption; } public static Option CreateRoutingTopologyTypeOption() { - var routingTopologyTypeOption = new Option( - name: "--routingTopology", - description: $"Specifies which routing topology to use.", - getDefaultValue: () => RoutingTopologyType.Conventional); - - routingTopologyTypeOption.AddAlias("-r"); + var routingTopologyTypeOption = new Option("--routingTopology", "-r") + { + Description = "Specifies which routing topology to use.", + DefaultValueFactory = _ => RoutingTopologyType.Conventional + }; return routingTopologyTypeOption; } public static Option CreateUseDurableEntitiesOption() { - var useDurableEntities = new Option( - name: "--useDurableEntities", - description: $"Specifies if entities should be created as durable", - getDefaultValue: () => true); - - useDurableEntities.AddAlias("-d"); + var useDurableEntities = new Option("--useDurableEntities", "-d") + { + Description = "Specifies if entities should be created as durable", + DefaultValueFactory = _ => true + }; return useDurableEntities; } public static Option CreateQueueTypeOption() { - var queueTypeOption = new Option( - name: "--queueType", - description: $"Specifies queue type will be used for queue creation", - getDefaultValue: () => QueueType.Quorum); - - queueTypeOption.AddAlias("-t"); + var queueTypeOption = new Option("--queueType", "-t") + { + Description = "Specifies queue type will be used for queue creation", + DefaultValueFactory = _ => QueueType.Quorum + }; return queueTypeOption; } public static Option CreateDisableCertValidationOption() { - var disableCertValidationOption = new Option( - name: "--disableCertValidation", - description: $"Disable remote certificate validation when connecting to the broker", - getDefaultValue: () => false); + var disableCertValidationOption = new Option("--disableCertValidation") + { + Description = "Disable remote certificate validation when connecting to the broker", + DefaultValueFactory = _ => false + }; return disableCertValidationOption; } public static Option CreateUseExternalAuthOption() { - var useExternalAuthOption = new Option( - name: "--useExternalAuth", - description: $"Use the external authorization option when connecting to the broker", - getDefaultValue: () => false); + var useExternalAuthOption = new Option("--useExternalAuth") + { + Description = "Use the external authorization option when connecting to the broker", + DefaultValueFactory = _ => false + }; return useExternalAuthOption; } public static Option CreateCertPathOption() { - var certPathOption = new Option( - name: "--certPath", - description: $"Set the path to the client certificate file for connecting to the broker"); + var certPathOption = new Option("--certPath") + { + Description = "Set the path to the client certificate file for connecting to the broker" + }; return certPathOption; } public static Option CreateCertPassphraseOption() { - var certPassphraseOption = new Option( - name: "--certPassphrase", - description: $"The passphrase for client certificate file for when using a client certificate"); + var certPassphraseOption = new Option("--certPassphrase") + { + Description = "The passphrase for client certificate file for when using a client certificate" + }; return certPassphraseOption; } @@ -140,15 +144,15 @@ public static BrokerConnectionBinder CreateBrokerConnectionBinderWithOptions(Com var disableCertValidationOption = CreateDisableCertValidationOption(); var useExternalAuthOption = CreateUseExternalAuthOption(); - command.AddOption(connectionStringOption); - command.AddOption(connectionStringEnvOption); - command.AddOption(managementApiUrlOption); - command.AddOption(managementApiUserNameOption); - command.AddOption(managementApiPasswordOption); - command.AddOption(certPathOption); - command.AddOption(certPassphraseOption); - command.AddOption(disableCertValidationOption); - command.AddOption(useExternalAuthOption); + command.Options.Add(connectionStringOption); + command.Options.Add(connectionStringEnvOption); + command.Options.Add(managementApiUrlOption); + command.Options.Add(managementApiUserNameOption); + command.Options.Add(managementApiPasswordOption); + command.Options.Add(certPathOption); + command.Options.Add(certPassphraseOption); + command.Options.Add(disableCertValidationOption); + command.Options.Add(useExternalAuthOption); return new BrokerConnectionBinder(connectionStringOption, connectionStringEnvOption, managementApiUrlOption, managementApiUserNameOption, managementApiPasswordOption, certPathOption, certPassphraseOption, disableCertValidationOption, useExternalAuthOption); } @@ -162,12 +166,12 @@ public static BrokerVerifierBinder CreateBrokerVerifierBinderWithOptions(Command var managementApiPasswordOption = CreateManagementApiPasswordOption(); var disableCertValidationOption = CreateDisableCertValidationOption(); - command.AddOption(connectionStringOption); - command.AddOption(connectionStringEnvOption); - command.AddOption(managementApiUrlOption); - command.AddOption(managementApiUserNameOption); - command.AddOption(managementApiPasswordOption); - command.AddOption(disableCertValidationOption); + command.Options.Add(connectionStringOption); + command.Options.Add(connectionStringEnvOption); + command.Options.Add(managementApiUrlOption); + command.Options.Add(managementApiUserNameOption); + command.Options.Add(managementApiPasswordOption); + command.Options.Add(disableCertValidationOption); return new BrokerVerifierBinder(connectionStringOption, connectionStringEnvOption, managementApiUrlOption, managementApiUserNameOption, managementApiPasswordOption, disableCertValidationOption); } @@ -178,9 +182,9 @@ public static RoutingTopologyBinder CreateRoutingTopologyBinderWithOptions(Comma var useDurableEntitiesOption = CreateUseDurableEntitiesOption(); var queueTypeOption = CreateQueueTypeOption(); - command.AddOption(routingTopologyTypeOption); - command.AddOption(useDurableEntitiesOption); - command.AddOption(queueTypeOption); + command.Options.Add(routingTopologyTypeOption); + command.Options.Add(useDurableEntitiesOption); + command.Options.Add(queueTypeOption); return new RoutingTopologyBinder(routingTopologyTypeOption, useDurableEntitiesOption, queueTypeOption); }