Skip to content
61 changes: 33 additions & 28 deletions src/AzureCloudServiceBus/AzureCloudServiceBusResource.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
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;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;

namespace Squadron
namespace Squadron
{
/// <summary>
/// Defines a Azure Cloud ServiceBus namespace
/// Defines a Azure Cloud ServiceBus namespace
/// </summary>
/// <typeparam name="TOptions">Option to initialize the resource</typeparam>
/// <typeparam name="TOptions">Option to initialize the resource</typeparam>
public class AzureCloudServiceBusResource<TOptions>
: AzureResource<TOptions>, IAsyncLifetime
where TOptions : AzureCloudServiceBusOptions,
Expand All @@ -26,10 +26,10 @@ public class AzureCloudServiceBusResource<TOptions>
private ServiceBusModel _serviceBusModel;
private readonly IMessageSink _messageSink;

/// <summary>
/// Initializes a new instance of the <see cref="AzureCloudServiceBusResource{TOptions}"/> class.
/// </summary>
/// <param name="messageSink">The message sink.</param>
/// <summary>
/// Initializes a new instance of the <see cref="AzureCloudServiceBusResource{TOptions}"/> class.
/// </summary>
/// <param name="messageSink">The message sink.</param>
public AzureCloudServiceBusResource(IMessageSink messageSink)
{
_messageSink = messageSink;
Expand All @@ -54,11 +54,11 @@ public ITopicClient GetTopicClient(string name,
}


/// <summary>
/// Creates a new topic
/// </summary>
/// <param name="configure">The builder.</param>
/// <returns>Client to access the created topic</returns>
/// <summary>
/// Creates a new topic
/// </summary>
/// <param name="configure">The builder.</param>
/// <returns>Client to access the created topic</returns>
public async Task<ITopicClient> CreateTopicAsync(Action<ServiceBusTopicBuilder> configure)
{
var builder = ServiceBusTopicBuilder.New();
Expand Down Expand Up @@ -133,9 +133,9 @@ private string GetQueue(string name)
return queue.CreatedName;
}

/// <summary>
/// Initialize the resource
/// </summary>
/// <summary>
/// Initialize the resource
/// </summary>
public override async Task InitializeAsync()
{
await base.InitializeAsync();
Expand All @@ -149,7 +149,7 @@ public override async Task InitializeAsync()
}

private void BuildOptions()
{
{
var builder = ServiceBusOptionsBuilder.New();
var options = new TOptions();
options.Configure(builder);
Expand All @@ -158,7 +158,7 @@ private void BuildOptions()
}

private void InitializeServiceBusManager()
{
{
_serviceBusManager = new ServiceBusManager(
AzureConfig.Credentials,
new AzureResourceIdentifier
Expand All @@ -170,17 +170,22 @@ private void InitializeServiceBusManager()
}

private async Task PrepareNamespaceAsync()
{
{
if (_serviceBusModel.Namespace == null)
{
_serviceBusModel.ProvisioningMode = ServiceBusProvisioningMode.CreateAndDelete;
_serviceBusModel.Namespace = await
_serviceBusManager.CreateNamespaceAsync(AzureConfig.DefaultLocation);
_serviceBusManager.CreateRandomNamespaceAsync(AzureConfig.DefaultLocation);
}
else if (_serviceBusModel.ProvisioningMode == ServiceBusProvisioningMode.CreateIfNotExists)
{
_serviceBusModel.Namespace = await
_serviceBusManager.CreateNamespaceIfNotExistsAsync(AzureConfig.DefaultLocation, _serviceBusModel.Namespace);
}
}

private async Task PrepareQueuesAsync()
{
{
foreach (ServiceBusQueueModel queue in _serviceBusModel.Queues)
{
if (_serviceBusModel.ProvisioningMode == ServiceBusProvisioningMode.UseExisting)
Expand All @@ -196,7 +201,7 @@ private async Task PrepareQueuesAsync()
}

private async Task PrepareTopicsAsync()
{
{
foreach (ServiceBusTopicModel topic in _serviceBusModel.Topics)
{
_messageSink.OnMessage(
Expand All @@ -220,8 +225,8 @@ private async Task<string> CreateTopicAsync(ServiceBusTopicModel topic)
}

/// <summary>
/// Cleans up the resource
/// </summary>
/// Cleans up the resource
/// </summary>
public async Task DisposeAsync()
{
try
Expand All @@ -248,5 +253,5 @@ public async Task DisposeAsync()
//do not fail test
}
}
}
}
}
}
81 changes: 43 additions & 38 deletions src/AzureCloudServiceBus/Model/ServiceBusModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,65 @@

namespace Squadron
{
/// <summary>
/// Azure ServiceBus model
/// </summary>
public class ServiceBusModel
/// <summary>
/// Azure ServiceBus model
/// </summary>
public class ServiceBusModel
{
/// <summary>
/// Gets or sets the namespace.
/// </summary>
/// <value>
/// The namespace.
/// </value>
/// <summary>
/// Gets or sets the namespace.
/// </summary>
/// <value>
/// The namespace.
/// </value>
public string Namespace { get; set; }

/// <summary>
/// Gets or sets the topics.
/// </summary>
/// <value>
/// The topics.
/// </value>
/// <summary>
/// Gets or sets the topics.
/// </summary>
/// <value>
/// The topics.
/// </value>
public List<ServiceBusTopicModel> Topics { get; set; }
= new List<ServiceBusTopicModel>();

/// <summary>
/// Gets or sets the queues.
/// </summary>
/// <value>
/// The queues.
/// </value>
/// <summary>
/// Gets or sets the queues.
/// </summary>
/// <value>
/// The queues.
/// </value>
public List<ServiceBusQueueModel> Queues { get; set; }
= new List<ServiceBusQueueModel>();

/// <summary>
/// Gets or sets the provisioning mode.
/// </summary>
/// <value>
/// The provisioning mode.
/// </value>
/// <summary>
/// Gets or sets the provisioning mode.
/// </summary>
/// <value>
/// The provisioning mode.
/// </value>
internal ServiceBusProvisioningMode ProvisioningMode { get; set; }
= ServiceBusProvisioningMode.UseExisting;
}

/// <summary>
/// Defines ServiceBUs provisioning modes
/// </summary>
/// <summary>
/// Defines ServiceBUs provisioning modes
/// </summary>
internal enum ServiceBusProvisioningMode
{
/// <summary>
/// The uan existing Azure resource
/// </summary>
/// <summary>
/// The uan existing Azure resource
/// </summary>
UseExisting,

/// <summary>
/// Provision and delete resource
/// </summary>
CreateAndDelete
/// <summary>
/// Provision and delete resource
/// </summary>
CreateAndDelete,

/// <summary>
/// Create or update resource
/// </summary>
CreateIfNotExists
}
}
42 changes: 39 additions & 3 deletions src/AzureCloudServiceBus/ServiceBusManager.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -38,19 +38,55 @@ private async Task EnsureAuthenticatedAsync()
}
}

internal async Task<string> CreateNamespaceAsync(string location)
internal async Task<string> CreateRandomNamespaceAsync(string location)
{
await EnsureAuthenticatedAsync();

var ns = $"squadron-{Guid.NewGuid().ToString("N").Substring(8)}";
return await CreateNamespaceAsync(location, ns);
}

internal async Task<string> CreateNamespaceIfNotExistsAsync(string location, string serviceBusNamespace)
{
await EnsureAuthenticatedAsync();

if (await CheckServiceBusNamespaceExists(serviceBusNamespace) == false)
return await CreateNamespaceAsync(location, serviceBusNamespace);

_identifier.Name = serviceBusNamespace;
return serviceBusNamespace;
}

private async Task<bool> CheckServiceBusNamespaceExists(string serviceBusNamespace)
{
var serviceBusNamespaces = new List<SBNamespace>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Insead of listing all the service bus namespaces I would use _client.Namespaces.CheckNameAvailabilityMethodAsync

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and we have been there as well. The reason why we didn't go with _client.Namespaces.CheckNameAvailabilityMethodAsync is that it will return the global availability of a name. In this context, we need to know the availability of a name given a resource group.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ServiceBus namespace name must be global available because has a public endpoint.
https://docs.microsoft.com/en-us/rest/api/servicebus/create-namespace ...the name must be unique across Azure to be successfully created

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@glucaci did you see my comment here?


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<string> CreateNamespaceAsync(string location, string ns)
{
var pars = new SBNamespace
{
Sku = new SBSku(SkuName.Standard),
Location = location
};

SBNamespace res = await _client.Namespaces
.CreateOrUpdateAsync(_identifier.ResourceGroupName, ns, pars);
.CreateOrUpdateAsync(_identifier.ResourceGroupName, ns, pars);

_identifier.Name = res.Name;
return res.Name;
Expand Down
Loading