Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support referencing existing Service Bus resources in references #7249

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

captainsafia
Copy link
Member

Opening this PR to sanity check the approach before adding it to other resource types. Some things of note:

  • Support for referencing existing resources in other subscription IDs/resource groups isn't implemented due to a gap in the underlying provisioning APIs. See [FEATURE REQ] FromExisting methods should support setting scopes Azure/azure-sdk-for-net#47980. There are workarounds for the but the juice might not be worth the squeeze.
  • Separate APIs for publishing/running with an existing resource backed by an annotation and explicit checks in the ConfigureInfrastructure calls for each resource.

@davidfowl
Copy link
Member

Opening this PR to sanity check the approach before adding it to other resource types. Some things of note:

This should be a draft right?

/// Represents a resource that is not managed by Aspire's provisioning or
/// container management layer.
/// </summary>
public class ExistingResourceAnnotation(string name, bool isPublishMode = false) : IResourceAnnotation
Copy link
Member

Choose a reason for hiding this comment

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

Is this supposed to be Azure specific?

Copy link
Member Author

Choose a reason for hiding this comment

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

Based on what we decided, yes. Are you indicating that the type name should indicate it's an ExistingAzureResourceAnnotation?

Copy link
Member

Choose a reason for hiding this comment

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

It's in the wrong assembly.

Copy link
Member

Choose a reason for hiding this comment

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

This should be ExistingAzureResourceAnnotation and it needs the subscription id, resource group and name as properties.

Copy link
Member Author

Choose a reason for hiding this comment

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

I remove subscription ID and resource group from this model because of:

Support for referencing existing resources in other subscription IDs/resource groups isn't implemented due to a gap in the underlying provisioning APIs. See Azure/azure-sdk-for-net#47980. There are workarounds for the but the juice might not be worth the squeeze.

We can't materialize these into scopes in the existing reference in the Bicep identifier at the moment so it doesn't make sense to expose an API for something that isn't supported at the moment.

We can add them later easily enough but if we have them now they'd be non-functional.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should at a minimum add the resource group so that we can play with what it would look like in a custom resource even though the Azure.Provisioning libs can't directly do it yet (we can try it out with the bicep resource). The reason it's important is because in a normal aspire deployment, the resource group is created on the fly, so at a minimum, using exisiting resource would look like a name + a different resource group in the same subscription.

Copy link
Member Author

Choose a reason for hiding this comment

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

The reason it's important is because in a normal aspire deployment, the resource group is created on the fly, so at a minimum, using exisiting resource would look like a name + a different resource group in the same subscription.

Hmmm...I see. Are you cool with iterating on this in a separate PR?

Copy link
Member

Choose a reason for hiding this comment

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

No, I think this pr is where we iterate

@@ -291,13 +291,13 @@ private async Task ProcessResourceAsync(IConfiguration configuration, Lazy<Task<

resourceLogger.LogWarning("No provisioner found for {resourceType} skipping.", resource.GetType().Name);
}
else if (!provisioner.ShouldProvision(configuration, resource.AzureResource))
else if (!resource.AzureResource.IsExisting() && !provisioner.ShouldProvision(configuration, resource.AzureResource))
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need these checks?

Copy link
Member Author

Choose a reason for hiding this comment

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

To avoid provisioning a resource or reading the config that has been cached in user-secrets when the same config name is stored. Without this, starting with:

var bus = builder.AddServiceBus("messages");

And moving to:

var bus = builder.AddServiceBus("messages").RunAsExisting("cached-messaging");

would still load the config for the Aspire-managed "messages" resource from user-secrets.

Copy link
Member

@davidfowl davidfowl Jan 27, 2025

Choose a reason for hiding this comment

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

Shouldn't that just work? I'm confused why we need this.

{
ArgumentNullException.ThrowIfNull(resource);

existingAzureResourceAnnotation = resource.Annotations.OfType<ExistingAzureResourceAnnotation>().FirstOrDefault();
Copy link
Member

Choose a reason for hiding this comment

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

For annotaitons, we usually have either:

  • respect multiple (not applicable here)
  • expect single
  • Last one wins

public static bool TryGetContainerImageName(this IResource resource, [NotNullWhen(true)] out string? imageName)
{
if (resource.Annotations.OfType<ContainerImageAnnotation>().LastOrDefault() is { } imageAnnotation)

I don't think using the "first" one is typical.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point -- the intent here was to follow the expect single pattern.

}

[Fact]
public async Task RequiresPublishAsExistingInPublishMode()
Copy link
Member

Choose a reason for hiding this comment

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

(nit) Is this test name correct? the test calls RunAsExisting

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, the test name is correct although confusing. I was intending to say that you need to call PublishAsExisting in publish-mode otherwise the API effectively no-ops.

}

[Fact]
public async Task AddExistingAzureServiceBusInPublishModeMode()
Copy link
Member

Choose a reason for hiding this comment

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

(nit) InPublishModeMode?

{
Sku = new AzureProvisioning.ServiceBusSku()
var existingResourceName = existingAnnotation.NameParameter.AsProvisioningParameter(infrastructure, $"{infrastructure.AspireResource.GetBicepIdentifier()}ExistingResourceName");
Copy link
Member

Choose a reason for hiding this comment

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

nit - Is ExistingResourceName too verbose?

@@ -86,7 +96,7 @@ public static IResourceBuilder<AzureServiceBusResource> AddAzureServiceBus(this
infrastructure.Add(cdkRule);
Copy link
Member

Choose a reason for hiding this comment

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

Does generating these Queues and Topics work with an existing resource?

Comment on lines +12 to +18
public class ExistingAzureResourceAnnotation(IResourceBuilder<ParameterResource> nameParameter) : IResourceAnnotation
{
/// <summary>
/// Gets the name of the existing resource.
/// </summary>
public IResourceBuilder<ParameterResource> NameParameter { get; } = nameParameter;
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public class ExistingAzureResourceAnnotation(IResourceBuilder<ParameterResource> nameParameter) : IResourceAnnotation
{
/// <summary>
/// Gets the name of the existing resource.
/// </summary>
public IResourceBuilder<ParameterResource> NameParameter { get; } = nameParameter;
}
public class ExistingAzureResourceAnnotation(ParameterResource nameParameter) : IResourceAnnotation
{
/// <summary>
/// Gets the name of the existing resource.
/// </summary>
public ParameterResource NameParameter { get; } = nameParameter;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't have an AsProvisioningParameter overload that takes the ParameterResource directly although that seems like an oversight given what the implementation currently does.

public static ProvisioningParameter AsProvisioningParameter(this IResourceBuilder<ParameterResource> parameterResourceBuilder, AzureResourceInfrastructure infrastructure, string? parameterName = null)
{
ArgumentNullException.ThrowIfNull(parameterResourceBuilder);
ArgumentNullException.ThrowIfNull(infrastructure);
parameterName ??= Infrastructure.NormalizeBicepIdentifier(parameterResourceBuilder.Resource.Name);
infrastructure.AspireResource.Parameters[parameterName] = parameterResourceBuilder.Resource;
return GetOrAddParameter(infrastructure, parameterName, parameterResourceBuilder.Resource.Secret);
}

I think we can probably just add another overload that takes ParameterResource and have the existing one call into it.

@danmoseley danmoseley requested review from Copilot and removed request for Copilot January 28, 2025 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants