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

Loose objects with strong views #7251

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public static IResourceBuilder<AzureAppConfigurationResource> AddAzureAppConfigu
};

var resource = new AzureAppConfigurationResource(name, configureInfrastructure);
return builder.AddResource(resource)
.WithManifestPublishingCallback(resource.WriteToManifest);
return builder.AddResource(resource);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public async Task BeforeStartAsync(DistributedApplicationModel appModel, Cancell
continue;
}

if (!r.IsContainer() && r is not ProjectResource)
if ((r.IsContainer() || r.IsProject()) is false)
{
continue;
}
Expand Down Expand Up @@ -303,7 +303,7 @@ private void ProcessEndpoints()

// We only keep track of schemes for project resources, since we don't want
// a non-project scheme to affect what project endpoints are considered default.
if (resource is ProjectResource && IsHttpScheme(endpoint.UriScheme))
if (resource.IsProject() && IsHttpScheme(endpoint.UriScheme))
{
httpSchemesEncountered.Add(endpoint.UriScheme);
}
Expand Down Expand Up @@ -405,7 +405,7 @@ static bool Compatible(string[] schemes) =>
// We're processed the http ingress, remove it from the list
endpointsByTargetPort.Remove(httpIngress);

var targetPort = httpIngress.Port ?? (resource is ProjectResource ? null : 80);
var targetPort = httpIngress.Port ?? (resource.IsProject() ? null : 80);

_httpIngress = (targetPort, httpIngress.AnyH2, httpIngress.External);

Expand Down Expand Up @@ -643,7 +643,7 @@ BicepValue<string> GetHostValue(string? prefix = null, string? suffix = null)
return await ProcessValueAsync(csrs.ConnectionStringExpression, executionContext, cancellationToken, secretType: secretType, parent: parent).ConfigureAwait(false);
}

if (value is ParameterResource param)
if (value is IResource r && r.TryGetParameter(out var param))
{
var st = param.Secret ? SecretType.Normal : secretType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ namespace Aspire.Hosting.Azure;
/// Wraps an <see cref="AzureCosmosDBResource" /> in a type that exposes container extension methods.
/// </summary>
/// <param name="innerResource">The inner resource used to store annotations.</param>
public class AzureCosmosDBEmulatorResource(AzureCosmosDBResource innerResource) : ContainerResource(innerResource.Name), IResource
public class AzureCosmosDBEmulatorResource(AzureCosmosDBResource innerResource) : ContainerResource(innerResource.Name, innerResource.Annotations), IResource
{
internal AzureCosmosDBResource InnerResource { get; } = innerResource;

/// <inheritdoc />
public override ResourceAnnotationCollection Annotations => InnerResource.Annotations;
public override ResourceAnnotationCollection Annotations => base.Annotations;
}
23 changes: 10 additions & 13 deletions src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,15 @@ private static IResourceBuilder<AzureCosmosDBResource> RunAsEmulator(this IResou
}

var scheme = useVNextPreview ? "http" : null;
builder.WithEndpoint(name: "emulator", scheme: scheme, targetPort: 8081)
.WithAnnotation(new ContainerImageAnnotation
{
Registry = CosmosDBEmulatorContainerImageTags.Registry,
Image = CosmosDBEmulatorContainerImageTags.Image,
Tag = useVNextPreview ? CosmosDBEmulatorContainerImageTags.TagVNextPreview : CosmosDBEmulatorContainerImageTags.Tag
});

var emulator = new AzureCosmosDBEmulatorResource(builder.Resource);
var emulatorBuilder = builder.ApplicationBuilder.CreateResourceBuilder(emulator);

emulatorBuilder
.WithImage(CosmosDBEmulatorContainerImageTags.Image)
.WithImageTag(useVNextPreview ? CosmosDBEmulatorContainerImageTags.TagVNextPreview : CosmosDBEmulatorContainerImageTags.Tag)
.WithImageRegistry(CosmosDBEmulatorContainerImageTags.Registry)
.WithEndpoint(name: "emulator", scheme: scheme, targetPort: 8081);

CosmosClient? cosmosClient = null;

Expand Down Expand Up @@ -125,12 +127,7 @@ private static IResourceBuilder<AzureCosmosDBResource> RunAsEmulator(this IResou

builder.WithHealthCheck(healthCheckKey);

if (configureContainer != null)
{
var surrogate = new AzureCosmosDBEmulatorResource(builder.Resource);
var surrogateBuilder = builder.ApplicationBuilder.CreateResourceBuilder(surrogate);
configureContainer(surrogateBuilder);
}
configureContainer?.Invoke(emulatorBuilder);

return builder;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@ namespace Aspire.Hosting.Azure;
/// Wraps an <see cref="AzureEventHubsResource" /> in a type that exposes container extension methods.
/// </summary>
/// <param name="innerResource">The inner resource used to store annotations.</param>
public class AzureEventHubsEmulatorResource(AzureEventHubsResource innerResource) : ContainerResource(innerResource.Name), IResource
public class AzureEventHubsEmulatorResource(AzureEventHubsResource innerResource) : ContainerResource(innerResource.Name, innerResource.Annotations), IResource
{
// The path to the emulator configuration file in the container.
internal const string EmulatorConfigJsonPath = "/Eventhubs_Emulator/ConfigFiles/Config.json";

private readonly AzureEventHubsResource _innerResource = innerResource;

/// <inheritdoc/>
public override string Name => _innerResource.Name;
public override string Name => base.Name;

/// <inheritdoc />
public override ResourceAnnotationCollection Annotations => _innerResource.Annotations;
public override ResourceAnnotationCollection Annotations => base.Annotations;
}
151 changes: 70 additions & 81 deletions src/Aspire.Hosting.Azure.EventHubs/AzureEventHubsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,22 +154,22 @@ public static IResourceBuilder<AzureEventHubsResource> RunAsEmulator(this IResou
// Create a default file mount. This could be replaced by a user-provided file mount.
var configHostFile = Path.Combine(Directory.CreateTempSubdirectory("AspireEventHubsEmulator").FullName, "Config.json");

var emulator = new AzureEventHubsEmulatorResource(builder.Resource);
var emulatorBuilder = builder.ApplicationBuilder.CreateResourceBuilder(emulator);

var defaultConfigFileMount = new ContainerMountAnnotation(
configHostFile,
AzureEventHubsEmulatorResource.EmulatorConfigJsonPath,
ContainerMountType.BindMount,
isReadOnly: true);

builder.WithAnnotation(defaultConfigFileMount);
emulatorBuilder.WithAnnotation(defaultConfigFileMount);

builder
.WithEndpoint(name: "emulator", targetPort: 5672)
.WithAnnotation(new ContainerImageAnnotation
{
Registry = EventHubsEmulatorContainerImageTags.Registry,
Image = EventHubsEmulatorContainerImageTags.Image,
Tag = EventHubsEmulatorContainerImageTags.Tag
});
emulatorBuilder
.WithImage(EventHubsEmulatorContainerImageTags.Image)
.WithImageTag(EventHubsEmulatorContainerImageTags.Tag)
.WithImageRegistry(EventHubsEmulatorContainerImageTags.Registry)
.WithEndpoint(name: "emulator", targetPort: 5672);

// Create a separate storage emulator for the Event Hub one
var storageResource = builder.ApplicationBuilder
Expand All @@ -178,15 +178,15 @@ public static IResourceBuilder<AzureEventHubsResource> RunAsEmulator(this IResou

var storage = storageResource.Resource;

builder.WithAnnotation(new EnvironmentCallbackAnnotation((EnvironmentCallbackContext context) =>
emulatorBuilder.WithEnvironment(context =>
{
var blobEndpoint = storage.GetEndpoint("blob");
var tableEndpoint = storage.GetEndpoint("table");

context.EnvironmentVariables.Add("ACCEPT_EULA", "Y");
context.EnvironmentVariables.Add("BLOB_SERVER", $"{blobEndpoint.Resource.Name}:{blobEndpoint.TargetPort}");
context.EnvironmentVariables.Add("METADATA_SERVER", $"{tableEndpoint.Resource.Name}:{tableEndpoint.TargetPort}");
}));
});

EventHubProducerClient? client = null;

Expand Down Expand Up @@ -217,95 +217,84 @@ public static IResourceBuilder<AzureEventHubsResource> RunAsEmulator(this IResou

builder.WithHealthCheck(healthCheckKey);

if (configureContainer != null)
{
var surrogate = new AzureEventHubsEmulatorResource(builder.Resource);
var surrogateBuilder = builder.ApplicationBuilder.CreateResourceBuilder(surrogate);
configureContainer(surrogateBuilder);
}
configureContainer?.Invoke(emulatorBuilder);

builder.ApplicationBuilder.Eventing.Subscribe<BeforeResourceStartedEvent>(builder.Resource, (e, ct) =>
{
var eventHubsEmulatorResources = builder.ApplicationBuilder.Resources.OfType<AzureEventHubsResource>().Where(x => x is { } eventHubsResource && eventHubsResource.IsEmulator);
if (!builder.Resource.IsEmulator)
{
return Task.CompletedTask;
}

var configFileMount = e.Resource.Annotations.OfType<ContainerMountAnnotation>().Single(v => v.Target == AzureEventHubsEmulatorResource.EmulatorConfigJsonPath);

if (!eventHubsEmulatorResources.Any())
// If there is a custom mount for EmulatorConfigJsonPath we don't need to create the Config.json file.
if (configFileMount != defaultConfigFileMount)
{
// No-op if there is no Azure Event Hubs emulator resource.
return Task.CompletedTask;
}

foreach (var emulatorResource in eventHubsEmulatorResources)
var fileStreamOptions = new FileStreamOptions() { Mode = FileMode.Create, Access = FileAccess.Write };

if (!OperatingSystem.IsWindows())
{
var configFileMount = emulatorResource.Annotations.OfType<ContainerMountAnnotation>().Single(v => v.Target == AzureEventHubsEmulatorResource.EmulatorConfigJsonPath);
fileStreamOptions.UnixCreateMode =
UnixFileMode.UserRead | UnixFileMode.UserWrite
| UnixFileMode.GroupRead | UnixFileMode.GroupWrite
| UnixFileMode.OtherRead | UnixFileMode.OtherWrite;
}

// If there is a custom mount for EmulatorConfigJsonPath we don't need to create the Config.json file.
if (configFileMount != defaultConfigFileMount)
{
continue;
}
using (var stream = new FileStream(configFileMount.Source!, fileStreamOptions))
{
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });

writer.WriteStartObject(); // {
writer.WriteStartObject("UserConfig"); // "UserConfig": {
writer.WriteStartArray("NamespaceConfig"); // "NamespaceConfig": [
writer.WriteStartObject(); // {
writer.WriteString("Type", "EventHub");

var fileStreamOptions = new FileStreamOptions() { Mode = FileMode.Create, Access = FileAccess.Write };
// This name is currently required by the emulator
writer.WriteString("Name", "emulatorNs1");
writer.WriteStartArray("Entities"); // "Entities": [

if (!OperatingSystem.IsWindows())
foreach (var hub in builder.Resource.Hubs)
{
fileStreamOptions.UnixCreateMode =
UnixFileMode.UserRead | UnixFileMode.UserWrite
| UnixFileMode.GroupRead | UnixFileMode.GroupWrite
| UnixFileMode.OtherRead | UnixFileMode.OtherWrite;
writer.WriteStartObject();
hub.WriteJsonObjectProperties(writer);
writer.WriteEndObject(); // }
}

using (var stream = new FileStream(configFileMount.Source!, fileStreamOptions))
{
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });

writer.WriteStartObject(); // {
writer.WriteStartObject("UserConfig"); // "UserConfig": {
writer.WriteStartArray("NamespaceConfig"); // "NamespaceConfig": [
writer.WriteStartObject(); // {
writer.WriteString("Type", "EventHub");

// This name is currently required by the emulator
writer.WriteString("Name", "emulatorNs1");
writer.WriteStartArray("Entities"); // "Entities": [

foreach (var hub in emulatorResource.Hubs)
{
writer.WriteStartObject();
hub.WriteJsonObjectProperties(writer);
writer.WriteEndObject(); // }
}

writer.WriteEndArray(); // ] (/Entities)
writer.WriteEndObject(); // }
writer.WriteEndArray(); // ], (/NamespaceConfig)
writer.WriteStartObject("LoggingConfig"); // "LoggingConfig": {
writer.WriteString("Type", "File"); // "Type": "File"
writer.WriteEndObject(); // } (/LoggingConfig)

writer.WriteEndObject(); // } (/UserConfig)
writer.WriteEndObject(); // } (/Root)
writer.WriteEndArray(); // ] (/Entities)
writer.WriteEndObject(); // }
writer.WriteEndArray(); // ], (/NamespaceConfig)
writer.WriteStartObject("LoggingConfig"); // "LoggingConfig": {
writer.WriteString("Type", "File"); // "Type": "File"
writer.WriteEndObject(); // } (/LoggingConfig)

}
writer.WriteEndObject(); // } (/UserConfig)
writer.WriteEndObject(); // } (/Root)

// Apply ConfigJsonAnnotation modifications
var configJsonAnnotations = emulatorResource.Annotations.OfType<ConfigJsonAnnotation>();
}

foreach (var annotation in configJsonAnnotations)
// Apply ConfigJsonAnnotation modifications
var configJsonAnnotations = e.Resource.Annotations.OfType<ConfigJsonAnnotation>();

foreach (var annotation in configJsonAnnotations)
{
using var readStream = new FileStream(configFileMount.Source!, FileMode.Open, FileAccess.Read);
var jsonObject = JsonNode.Parse(readStream);
readStream.Close();

using var writeStream = new FileStream(configFileMount.Source!, FileMode.Open, FileAccess.Write);
using var writer = new Utf8JsonWriter(writeStream, new JsonWriterOptions { Indented = true });

if (jsonObject == null)
{
using var readStream = new FileStream(configFileMount.Source!, FileMode.Open, FileAccess.Read);
var jsonObject = JsonNode.Parse(readStream);
readStream.Close();

using var writeStream = new FileStream(configFileMount.Source!, FileMode.Open, FileAccess.Write);
using var writer = new Utf8JsonWriter(writeStream, new JsonWriterOptions { Indented = true });

if (jsonObject == null)
{
throw new InvalidOperationException("The configuration file mount could not be parsed.");
}
annotation.Configure(jsonObject);
jsonObject.WriteTo(writer);
throw new InvalidOperationException("The configuration file mount could not be parsed.");
}
annotation.Configure(jsonObject);
jsonObject.WriteTo(writer);
}

return Task.CompletedTask;
Expand Down Expand Up @@ -365,7 +354,7 @@ public static IResourceBuilder<AzureEventHubsEmulatorResource> WithHostPort(this
/// <param name="path">Path to the file on the AppHost where the emulator configuration is located.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<AzureEventHubsEmulatorResource> WithConfigurationFile(this IResourceBuilder<AzureEventHubsEmulatorResource> builder, string path)
{
{
// Update the existing mount
var configFileMount = builder.Resource.Annotations.OfType<ContainerMountAnnotation>().LastOrDefault(v => v.Target == AzureEventHubsEmulatorResource.EmulatorConfigJsonPath);
if (configFileMount != null)
Expand Down
Loading
Loading