From 2196e277bc0ff8a6f6336e3c21b053b80f75f7ed Mon Sep 17 00:00:00 2001 From: Karsten Marcussen Date: Thu, 3 Jun 2021 15:58:39 +0200 Subject: [PATCH 1/4] Adding method for defining namespace with create or update program flow. --- .../AzureCloudServiceBusResource.cs | 60 +++++++------- .../Model/ServiceBusModel.cs | 81 ++++++++++--------- src/AzureCloudServiceBus/ServiceBusManager.cs | 17 ++++ .../ServiceBusOptionsBuilder.cs | 58 ++++++------- 4 files changed, 124 insertions(+), 92 deletions(-) diff --git a/src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs b/src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs index 5f19be31..d441cbe7 100644 --- a/src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs +++ b/src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs @@ -1,8 +1,8 @@ -using System; -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; +using System.Text; using System.Threading.Tasks; using Microsoft.Azure.ServiceBus; using Squadron.AzureCloud; @@ -10,12 +10,12 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace Squadron +namespace Squadron { /// - /// Defines a Azure Cloud ServiceBus namespace + /// Defines a Azure Cloud ServiceBus namespace /// - /// Option to initialize the resource + /// Option to initialize the resource public class AzureCloudServiceBusResource : AzureResource, IAsyncLifetime where TOptions : AzureCloudServiceBusOptions, @@ -26,10 +26,10 @@ public class AzureCloudServiceBusResource private ServiceBusModel _serviceBusModel; private readonly IMessageSink _messageSink; - /// - /// Initializes a new instance of the class. - /// - /// The message sink. + /// + /// Initializes a new instance of the class. + /// + /// The message sink. public AzureCloudServiceBusResource(IMessageSink messageSink) { _messageSink = messageSink; @@ -54,11 +54,11 @@ public ITopicClient GetTopicClient(string name, } - /// - /// Creates a new topic - /// - /// The builder. - /// Client to access the created topic + /// + /// Creates a new topic + /// + /// The builder. + /// Client to access the created topic public async Task CreateTopicAsync(Action configure) { var builder = ServiceBusTopicBuilder.New(); @@ -133,9 +133,9 @@ private string GetQueue(string name) return queue.CreatedName; } - /// - /// Initialize the resource - /// + /// + /// Initialize the resource + /// public override async Task InitializeAsync() { await base.InitializeAsync(); @@ -149,7 +149,7 @@ public override async Task InitializeAsync() } private void BuildOptions() - { + { var builder = ServiceBusOptionsBuilder.New(); var options = new TOptions(); options.Configure(builder); @@ -158,7 +158,7 @@ private void BuildOptions() } private void InitializeServiceBusManager() - { + { _serviceBusManager = new ServiceBusManager( AzureConfig.Credentials, new AzureResourceIdentifier @@ -170,17 +170,23 @@ private void InitializeServiceBusManager() } private async Task PrepareNamespaceAsync() - { + { if (_serviceBusModel.Namespace == null) { _serviceBusModel.ProvisioningMode = ServiceBusProvisioningMode.CreateAndDelete; _serviceBusModel.Namespace = await _serviceBusManager.CreateNamespaceAsync(AzureConfig.DefaultLocation); } + else if (_serviceBusModel.ProvisioningMode == ServiceBusProvisioningMode.CreateOrUpdate) + { + _serviceBusModel.Namespace = await + _serviceBusManager.CreateNamespaceAsync(AzureConfig.DefaultLocation, _serviceBusModel.Namespace); + } + } private async Task PrepareQueuesAsync() - { + { foreach (ServiceBusQueueModel queue in _serviceBusModel.Queues) { if (_serviceBusModel.ProvisioningMode == ServiceBusProvisioningMode.UseExisting) @@ -196,7 +202,7 @@ private async Task PrepareQueuesAsync() } private async Task PrepareTopicsAsync() - { + { foreach (ServiceBusTopicModel topic in _serviceBusModel.Topics) { _messageSink.OnMessage( @@ -220,8 +226,8 @@ private async Task CreateTopicAsync(ServiceBusTopicModel topic) } /// - /// Cleans up the resource - /// + /// Cleans up the resource + /// public async Task DisposeAsync() { try @@ -248,5 +254,5 @@ public async Task DisposeAsync() //do not fail test } } - } -} + } +} diff --git a/src/AzureCloudServiceBus/Model/ServiceBusModel.cs b/src/AzureCloudServiceBus/Model/ServiceBusModel.cs index 22ea1503..de7f57bc 100644 --- a/src/AzureCloudServiceBus/Model/ServiceBusModel.cs +++ b/src/AzureCloudServiceBus/Model/ServiceBusModel.cs @@ -4,60 +4,65 @@ namespace Squadron { - /// - /// Azure ServiceBus model - /// - public class ServiceBusModel + /// + /// Azure ServiceBus model + /// + public class ServiceBusModel { - /// - /// Gets or sets the namespace. - /// - /// - /// The namespace. - /// + /// + /// Gets or sets the namespace. + /// + /// + /// The namespace. + /// public string Namespace { get; set; } - /// - /// Gets or sets the topics. - /// - /// - /// The topics. - /// + /// + /// Gets or sets the topics. + /// + /// + /// The topics. + /// public List Topics { get; set; } = new List(); - /// - /// Gets or sets the queues. - /// - /// - /// The queues. - /// + /// + /// Gets or sets the queues. + /// + /// + /// The queues. + /// public List Queues { get; set; } = new List(); - /// - /// Gets or sets the provisioning mode. - /// - /// - /// The provisioning mode. - /// + /// + /// Gets or sets the provisioning mode. + /// + /// + /// The provisioning mode. + /// internal ServiceBusProvisioningMode ProvisioningMode { get; set; } = ServiceBusProvisioningMode.UseExisting; } - /// - /// Defines ServiceBUs provisioning modes - /// + /// + /// Defines ServiceBUs provisioning modes + /// internal enum ServiceBusProvisioningMode { - /// - /// The uan existing Azure resource - /// + /// + /// The uan existing Azure resource + /// UseExisting, - /// - /// Provision and delete resource - /// - CreateAndDelete + /// + /// Provision and delete resource + /// + CreateAndDelete, + + /// + /// Create or update resource + /// + CreateOrUpdate } } diff --git a/src/AzureCloudServiceBus/ServiceBusManager.cs b/src/AzureCloudServiceBus/ServiceBusManager.cs index 36223056..0c7bc692 100644 --- a/src/AzureCloudServiceBus/ServiceBusManager.cs +++ b/src/AzureCloudServiceBus/ServiceBusManager.cs @@ -56,6 +56,23 @@ internal async Task CreateNamespaceAsync(string location) return res.Name; } + internal async Task CreateNamespaceAsync(string location, string serviceBusNamespace) + { + await EnsureAuthenticatedAsync(); + + var pars = new SBNamespace + { + Sku = new SBSku(SkuName.Standard), + Location = location + }; + + SBNamespace res = await _client.Namespaces + .CreateOrUpdateAsync(_identifier.ResourceGroupName, serviceBusNamespace, pars); + + _identifier.Name = res.Name; + return res.Name; + } + internal async Task CreateTopic(ServiceBusTopicModel model) { await EnsureAuthenticatedAsync(); diff --git a/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs b/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs index d2c60d6b..cd694700 100644 --- a/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs +++ b/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs @@ -4,19 +4,19 @@ namespace Squadron { - /// - /// ServiceBusOptions builder - /// + /// + /// ServiceBusOptions builder + /// public class ServiceBusOptionsBuilder : AzureResourceOptionsBuilder { private ServiceBusModel _model = new ServiceBusModel(); private List _topics = new List(); private List _queues = new List(); - /// - /// Creates a new empty builder - /// - /// + /// + /// Creates a new empty builder + /// + /// public static ServiceBusOptionsBuilder New() => new ServiceBusOptionsBuilder(); @@ -26,23 +26,27 @@ private ServiceBusOptionsBuilder() } - /// - /// Namespace - /// - /// The namespace. - /// - public ServiceBusOptionsBuilder Namespace(string ns) + /// + /// Namespace + /// + /// The namespace. + /// creates namespace if doesn't exist. + /// + public ServiceBusOptionsBuilder Namespace(string ns, bool createOrUpdate = false) { _model.Namespace = ns; + _model.ProvisioningMode = createOrUpdate + ? ServiceBusProvisioningMode.CreateOrUpdate + : ServiceBusProvisioningMode.UseExisting; return this; } - /// - /// Adds a topic. - /// - /// The topic name. - /// + /// + /// Adds a topic. + /// + /// The topic name. + /// public ServiceBusTopicBuilder AddTopic(string name) { var topicBuilder = ServiceBusTopicBuilder.New(name); @@ -51,11 +55,11 @@ public ServiceBusTopicBuilder AddTopic(string name) } - /// - /// Adds a queue. - /// - /// The queue name. - /// + /// + /// Adds a queue. + /// + /// The queue name. + /// public ServiceBusQueueBuilder AddQueue(string name) { var queueBuilder = new ServiceBusQueueBuilder(name); @@ -64,10 +68,10 @@ public ServiceBusQueueBuilder AddQueue(string name) } - /// - /// Builds the options - /// - /// + /// + /// Builds the options + /// + /// public ServiceBusModel Build() { foreach (ServiceBusTopicBuilder tb in _topics) From 2c18477097d4dbc141e46b1376642a9ea856be28 Mon Sep 17 00:00:00 2001 From: HenrikSommer <54191286+HenrikSommer@users.noreply.github.com> Date: Fri, 4 Jun 2021 10:41:43 +0200 Subject: [PATCH 2/4] Added support for usage of existing Service Bus Namespace Use ServiceBusProvisioningMode CreateIfNotExists for automatic Service Bus Namespace creation. Service Bus Namespace creation is a slow process, thus reuse existing Namespace will highly speed up the creation process. --- .../AzureCloudServiceBusResource.cs | 7 ++--- .../Model/ServiceBusModel.cs | 2 +- src/AzureCloudServiceBus/ServiceBusManager.cs | 31 ++++++++++--------- .../ServiceBusOptionsBuilder.cs | 8 ++--- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs b/src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs index d441cbe7..8123682f 100644 --- a/src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs +++ b/src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs @@ -175,14 +175,13 @@ private async Task PrepareNamespaceAsync() { _serviceBusModel.ProvisioningMode = ServiceBusProvisioningMode.CreateAndDelete; _serviceBusModel.Namespace = await - _serviceBusManager.CreateNamespaceAsync(AzureConfig.DefaultLocation); + _serviceBusManager.CreateRandomNamespaceAsync(AzureConfig.DefaultLocation); } - else if (_serviceBusModel.ProvisioningMode == ServiceBusProvisioningMode.CreateOrUpdate) + else if (_serviceBusModel.ProvisioningMode == ServiceBusProvisioningMode.CreateIfNotExists) { _serviceBusModel.Namespace = await - _serviceBusManager.CreateNamespaceAsync(AzureConfig.DefaultLocation, _serviceBusModel.Namespace); + _serviceBusManager.CreateNamespaceIfNotExistsAsync(AzureConfig.DefaultLocation, _serviceBusModel.Namespace); } - } private async Task PrepareQueuesAsync() diff --git a/src/AzureCloudServiceBus/Model/ServiceBusModel.cs b/src/AzureCloudServiceBus/Model/ServiceBusModel.cs index de7f57bc..92544f46 100644 --- a/src/AzureCloudServiceBus/Model/ServiceBusModel.cs +++ b/src/AzureCloudServiceBus/Model/ServiceBusModel.cs @@ -63,6 +63,6 @@ internal enum ServiceBusProvisioningMode /// /// Create or update resource /// - CreateOrUpdate + CreateIfNotExists } } diff --git a/src/AzureCloudServiceBus/ServiceBusManager.cs b/src/AzureCloudServiceBus/ServiceBusManager.cs index 0c7bc692..4e2a0b8c 100644 --- a/src/AzureCloudServiceBus/ServiceBusManager.cs +++ b/src/AzureCloudServiceBus/ServiceBusManager.cs @@ -38,28 +38,31 @@ private async Task EnsureAuthenticatedAsync() } } - internal async Task CreateNamespaceAsync(string location) + internal async Task CreateRandomNamespaceAsync(string location) { await EnsureAuthenticatedAsync(); var ns = $"squadron-{Guid.NewGuid().ToString("N").Substring(8)}"; - var pars = new SBNamespace - { - Sku = new SBSku(SkuName.Standard), - Location = location - }; - - SBNamespace res = await _client.Namespaces - .CreateOrUpdateAsync(_identifier.ResourceGroupName, ns, pars); - - _identifier.Name = res.Name; - return res.Name; + return await CreateNamespaceAsync(location, ns); } - internal async Task CreateNamespaceAsync(string location, string serviceBusNamespace) + internal async Task CreateNamespaceIfNotExistsAsync(string location, string serviceBusNamespace) { await EnsureAuthenticatedAsync(); + var checkNameAvailability = new CheckNameAvailability {Name = serviceBusNamespace}; + var response = await _client.Namespaces + .CheckNameAvailabilityMethodWithHttpMessagesAsync(checkNameAvailability); + + if (response.Body.NameAvailable is true) + return await CreateNamespaceAsync(location, serviceBusNamespace); + + _identifier.Name = serviceBusNamespace; + return serviceBusNamespace; + } + + private async Task CreateNamespaceAsync(string location, string ns) + { var pars = new SBNamespace { Sku = new SBSku(SkuName.Standard), @@ -67,7 +70,7 @@ internal async Task CreateNamespaceAsync(string location, string service }; SBNamespace res = await _client.Namespaces - .CreateOrUpdateAsync(_identifier.ResourceGroupName, serviceBusNamespace, pars); + .CreateOrUpdateAsync(_identifier.ResourceGroupName, ns, pars); _identifier.Name = res.Name; return res.Name; diff --git a/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs b/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs index cd694700..4ec5106c 100644 --- a/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs +++ b/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs @@ -30,13 +30,13 @@ private ServiceBusOptionsBuilder() /// Namespace /// /// The namespace. - /// creates namespace if doesn't exist. + /// creates namespace if doesn't exist. /// - public ServiceBusOptionsBuilder Namespace(string ns, bool createOrUpdate = false) + public ServiceBusOptionsBuilder Namespace(string ns, bool createIfNotExists = false) { _model.Namespace = ns; - _model.ProvisioningMode = createOrUpdate - ? ServiceBusProvisioningMode.CreateOrUpdate + _model.ProvisioningMode = createIfNotExists + ? ServiceBusProvisioningMode.CreateIfNotExists : ServiceBusProvisioningMode.UseExisting; return this; } From 814586e337be0a852bb3aaec706bafea3847bfd7 Mon Sep 17 00:00:00 2001 From: HenrikSommer <54191286+HenrikSommer@users.noreply.github.com> Date: Fri, 4 Jun 2021 12:35:20 +0200 Subject: [PATCH 3/4] Added lookup for Service Bus Namespace within resource group --- src/AzureCloudServiceBus/ServiceBusManager.cs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/AzureCloudServiceBus/ServiceBusManager.cs b/src/AzureCloudServiceBus/ServiceBusManager.cs index 4e2a0b8c..331a6f2a 100644 --- a/src/AzureCloudServiceBus/ServiceBusManager.cs +++ b/src/AzureCloudServiceBus/ServiceBusManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Text; +using System.Linq; using System.Threading.Tasks; using Microsoft.Azure.Management.ServiceBus; using Microsoft.Azure.Management.ServiceBus.Models; @@ -50,17 +50,33 @@ internal async Task CreateNamespaceIfNotExistsAsync(string location, str { await EnsureAuthenticatedAsync(); - var checkNameAvailability = new CheckNameAvailability {Name = serviceBusNamespace}; - var response = await _client.Namespaces - .CheckNameAvailabilityMethodWithHttpMessagesAsync(checkNameAvailability); - - if (response.Body.NameAvailable is true) + if (await CheckServiceBusNamespaceExists(serviceBusNamespace) == false) return await CreateNamespaceAsync(location, serviceBusNamespace); _identifier.Name = serviceBusNamespace; return serviceBusNamespace; } + private async Task CheckServiceBusNamespaceExists(string serviceBusNamespace) + { + var serviceBusNamespaces = new List(); + + var listByResourceGroupAsync = await _client.Namespaces.ListByResourceGroupAsync(_identifier.ResourceGroupName); + serviceBusNamespaces.AddRange(listByResourceGroupAsync); + + var nextPageLink = listByResourceGroupAsync.NextPageLink; + while (nextPageLink != null) + { + listByResourceGroupAsync = await _client.Namespaces.ListByResourceGroupNextAsync(nextPageLink); + nextPageLink = listByResourceGroupAsync.Any(x => x.Name == serviceBusNamespace) ? + null : + listByResourceGroupAsync.NextPageLink; + serviceBusNamespaces.AddRange(listByResourceGroupAsync); + } + + return serviceBusNamespaces.Any(x => x.Name == serviceBusNamespace); + } + private async Task CreateNamespaceAsync(string location, string ns) { var pars = new SBNamespace From 6a6c9085afac75a952b9bf0ddae90a10faeb86ae Mon Sep 17 00:00:00 2001 From: Henrik Sommer Date: Wed, 30 Jun 2021 23:38:47 +0200 Subject: [PATCH 4/4] Reintroduce Namespace-method with one parameter Namespace-method with one parameter is reintroduced to preserve backward compatibility. --- src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs b/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs index 4ec5106c..8112c21f 100644 --- a/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs +++ b/src/AzureCloudServiceBus/ServiceBusOptionsBuilder.cs @@ -26,13 +26,23 @@ private ServiceBusOptionsBuilder() } + /// + /// Namespace + /// + /// The namespace. + /// + public ServiceBusOptionsBuilder Namespace(string ns) + { + return Namespace(ns, createIfNotExists: false); + } + /// /// Namespace /// /// The namespace. /// creates namespace if doesn't exist. /// - public ServiceBusOptionsBuilder Namespace(string ns, bool createIfNotExists = false) + public ServiceBusOptionsBuilder Namespace(string ns, bool createIfNotExists) { _model.Namespace = ns; _model.ProvisioningMode = createIfNotExists