Skip to content

Commit

Permalink
First stab at secret resource implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchdenny committed Dec 23, 2023
1 parent 2a0485a commit 473f6c9
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 0 deletions.
3 changes: 3 additions & 0 deletions samples/eShopLite/AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
var builder = DistributedApplication.CreateBuilder(args);

var apiKey = builder.AddSecretStore("secretstore").AddSecret("apikey");

var catalogDb = builder.AddPostgres("postgres").AddDatabase("catalogdb");

var basketCache = builder.AddRedis("basketcache");
Expand All @@ -16,6 +18,7 @@

builder.AddProject<Projects.MyFrontend>("frontend")
.WithReference(basketService)
.WithEnvironment("API_KEY", apiKey)
.WithReference(catalogService.GetEndpoint("http"));

builder.AddProject<Projects.OrderProcessor>("orderprocessor")
Expand Down
5 changes: 5 additions & 0 deletions samples/eShopLite/AppHost/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Secrets": {
"secretstore": {
"apikey": "fakeapikey"
}
}
}
15 changes: 15 additions & 0 deletions samples/eShopLite/AppHost/aspire-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
{
"resources": {
"secretstore": {
"type": "secrets.store.v0"
},
"apikey": {
"type": "secrets.secret.v0",
"parent": "secretstore",
"value": "{apikey.inputs.value}",
"inputs": {
"value": {
"type": "string",
"secret": "true"
}
}
},
"postgres": {
"type": "postgres.server.v0"
},
Expand Down Expand Up @@ -62,6 +76,7 @@
"env": {
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true",
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true",
"API_KEY": "{apikey.value}",
"services__basketservice__0": "{basketservice.bindings.http.url}",
"services__basketservice__1": "{basketservice.bindings.https.url}",
"services__catalogservice__0": "{catalogservice.bindings.http.url}"
Expand Down
9 changes: 9 additions & 0 deletions src/Aspire.Hosting/ApplicationModel/SecretResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Aspire.Hosting.ApplicationModel;

public class SecretResource(string name, SecretStoreResource parent) : Resource(name), IResourceWithParent<SecretStoreResource>
{
public SecretStoreResource Parent => parent;
}
8 changes: 8 additions & 0 deletions src/Aspire.Hosting/ApplicationModel/SecretStoreResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Aspire.Hosting.ApplicationModel;

public class SecretStoreResource(string name) : Resource(name)
{
}
59 changes: 59 additions & 0 deletions src/Aspire.Hosting/Extensions/SecretResourceBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Publishing;

namespace Aspire.Hosting;

public static class SecretResourceBuilderExtensions
{
public static IResourceBuilder<SecretStoreResource> AddSecretStore(this IDistributedApplicationBuilder builder, string name)
{
var resource = new SecretStoreResource(name);
return builder.AddResource(resource)
.WithManifestPublishingCallback(WriteSecretStoreToManifest);
}

private static void WriteSecretStoreToManifest(ManifestPublishingContext context)
{
context.Writer.WriteString("type", "secrets.store.v0");
}

public static IResourceBuilder<SecretResource> AddSecret(this IResourceBuilder<SecretStoreResource> builder, string name)
{
var resource = new SecretResource(name, builder.Resource);
return builder.ApplicationBuilder.AddResource(resource)
.WithManifestPublishingCallback(context => WriteSecretToManifest(context, resource));
}

private static void WriteSecretToManifest(ManifestPublishingContext context, SecretResource secret)
{
context.Writer.WriteString("type", "secrets.secret.v0");
context.Writer.WriteString("parent", secret.Parent.Name);
context.Writer.WriteString("value", $"{{{secret.Name}.inputs.value}}");
context.Writer.WriteStartObject("inputs");
context.Writer.WriteStartObject("value");
context.Writer.WriteString("type", "string");
context.Writer.WriteString("secret", "true");
context.Writer.WriteEndObject();
context.Writer.WriteEndObject();
}

public static IResourceBuilder<T> WithEnvironment<T>(this IResourceBuilder<T> builder, string name, IResourceBuilder<SecretResource> secret) where T: IResourceWithEnvironment
{
return builder.WithEnvironment(context =>
{
if (context.PublisherName == "manifest")
{
context.EnvironmentVariables[name] = $"{{{secret.Resource.Name}.value}}";
return;
}

var configurationKey = $"Secrets:{secret.Resource.Parent.Name}:{secret.Resource.Name}";

context.EnvironmentVariables[name] = builder.ApplicationBuilder.Configuration[configurationKey]
?? throw new DistributedApplicationException($"Environment variable '{name}' could not be added because configuration key '{configurationKey}' not present.");
});
}
}

0 comments on commit 473f6c9

Please sign in to comment.