Skip to content

Instantly share code, notes, and snippets.

@afscrome
Last active June 10, 2024 16:44
Show Gist options
  • Save afscrome/f6171fb34534f5e6f0439496da509cf7 to your computer and use it in GitHub Desktop.
Save afscrome/f6171fb34534f5e6f0439496da509cf7 to your computer and use it in GitHub Desktop.
Writing Config Files with Aspire
public record class ConfigFileContext(
DistributedApplicationModel AppModel,
Stream Stream,
CancellationToken CancellationToken
);
[DebuggerDisplay($"{{{nameof(Path)},nq}}")]
public class ConfigFileAnnotation(string path, Func<ConfigFileContext, Task> generator) : IResourceAnnotation
{
public FileInfo Path { get; } = new FileInfo(path);
public Func<ConfigFileContext, Task> Generator => generator;
}
public class ConfigFileWriterHook(DistributedApplicationExecutionContext _executionContext, ResourceLoggerService _resourceLoggerService) : IDistributedApplicationLifecycleHook
{
public async Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
{
if (_executionContext.IsPublishMode)
{
return;
}
foreach (var resource in appModel.Resources)
{
foreach (var annotation in resource.Annotations.OfType<ConfigFileAnnotation>())
{
var logger = _resourceLoggerService.GetLogger(resource.Name);
logger.LogInformation("Building Config File {File}", annotation.Path);
using var stream = annotation.Path.Open(FileMode.Create, FileAccess.Write, FileShare.None);
var context = new ConfigFileContext(appModel, stream, cancellationToken);
await annotation.Generator(context);
}
}
}
}
public static class ConfigFileExtensions
{
public static IResourceBuilder<T> WithConfigurationFile<T>(this IResourceBuilder<T> builder, string path, in ReferenceExpression.ExpressionInterpolatedStringHandler value)
where T : IResource
{
var expression = ReferenceExpression.Create(value);
return builder.WithConfigurationFile(path, async context =>
{
var stringValue = await expression.GetValueAsync(default);
using var writer = new StreamWriter(context.Stream);
await writer.WriteAsync(stringValue);
});
}
public static IResourceBuilder<T> WithConfigurationFile<T>(this IResourceBuilder<T> builder, string path, Func<ConfigFileContext, Task> generator)
where T : IResource
{
builder.ApplicationBuilder.Services.TryAddLifecycleHook<ConfigFileWriterHook>();
return builder.WithAnnotation(new ConfigFileAnnotation(path, generator));
}
}
// Building an xml file for an executable
builder
.AddExecutable("test", ...)
.WithConfigurationFile("c:\\temp\\foo.xml", $"""
<?xml version="1.0" encoding="utf-8"?>
<root>
<dbConnectionString>{database.Resource.ConnectionStringExpression}</dbConnectionString>
</root>
""");
//Adding a config file for use by a container, and mount it into the container.
// This example is for https://opentelemetry.io/docs/collector/configuration/
builder
.AddContainer(ResourceName, "otel/opentelemetry-collector-contrib", "0.91.0")
.WithBindMount("c:\\temp\\otel-config.yaml", $"{configContainerDir}/config.yaml")
.WithConfigurationFile("c:\\temp\\otel-config.yaml", async context =>
{
var config = BuildOtelConfig();
using var writer = new StreamWriter(context.Stream);
new YamlDotNet.Serialization.Serializer().Serialize(writer, config);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment