dotnet / aspire

An opinionated, cloud ready stack for building observable, production ready, distributed applications in .NET
https://learn.microsoft.com/dotnet/aspire
MIT License
3.58k stars 392 forks source link

AddParameter overloads that supports providing a value #4429

Open davidfowl opened 2 months ago

davidfowl commented 2 months ago

Background and Motivation

There are scenarios 2 scenarios that aren't supported well:

  1. Adding a parameter with a default value only used at runtime that doesn't come from configuration, or uses a different configuration schema that we expect by default (not Parameters:name).
  2. Adding a parameter that uses a default value at publish time (https://github.com/dotnet/aspire/pull/4102/files#r1594886611)

Proposed API

namespace Aspire.Hosting;

public static class ParameterResourceBuilderExtensions
{
+    public static IResourceBuilder<ParameterResource> AddParameter(this IDistribuedApplicationBuilder builder, string name, string value, bool secret = false);
+    public static IResourceBuilder<ParameterResource> AddParameter(this IDistribuedApplicationBuilder builder, string name, Func<string> valueGetter, bool secret = false);
+    public static IResourceBuilder<ParameterResource> AddParameterFromConfiguration(this IDistribuedApplicationBuilder builder, string name, string configurationKey, bool secret = false);
+    public static IResourceBuilder<ParameterResource> PublishDefaultValue(this IResourceBuilder<ParameterResource> parameter);
}

Usage Examples

var builder = DistributedApplication.CreateBuilder(args);

if (builder.ExecutionContext.IsRunMode)
{
    // Load key vault secrets, read the keyvault connection information from configuration
    builder.Configuration.AddAzureKeyVaultSecrets("sharedKv");
}

var accessToken = builder.AddParameterFromConfiguration("accessToken", "Auth:AccessToken", secret: true);

builder.AddProject<Projects.WebApplication1>("api")
       .WithEnvironment("AccessToken", accessToken)
       .WithExternalHttpEndpoints();

builder.Build().Run();

Default user name

var username = builder.AddParameter("pg-username", "admin").PublishDefaultValue();

builder.AddPostgres("pg", username: username);

Manifest changes

PublishDefaultValue requires a manifest schema change:

{
    "pg-username": {
      "type": "parameter.v0",
      "value": "{pg-username.inputs.value}",
      "inputs": {
        "value": {
          "type": "string",
+          "default": {
+              "value": "admin"
+          }
        }
      }
    }
]

Risks

Low

eerhardt commented 2 months ago

@vhvb1989 @ellismg - does azd support default values in the manifest as above? Or do we need to log an issue in azd to get this support?

eerhardt commented 2 months ago
  • public static IResourceBuilder AddParameter(this IDistribuedApplicationBuilder builder, string value, bool secret = false);
  • public static IResourceBuilder AddParameter(this IDistribuedApplicationBuilder builder, Func<Task> valueGetter, bool secret = false);
  • public static IResourceBuilder AddParameter(this IDistribuedApplicationBuilder builder, Func valueGetter, bool secret = false);
  1. Don't we need a string name parameter?
  2. Should value => defaultValue?
    public static IResourceBuilder<ParameterResource> AddParameter(this IDistribuedApplicationBuilder builder, string name, string defaultValue, bool secret = false);
    public static IResourceBuilder<ParameterResource> AddParameter(this IDistribuedApplicationBuilder builder, string name, Func<Task<string>> defaultValueGetter, bool secret = false);
    public static IResourceBuilder<ParameterResource> AddParameter(this IDistribuedApplicationBuilder builder, string name, Func<string> defaultValueGetter, bool secret = false);
  1. What happens if I have a configuration value with the name Parameters:<name>? Which wins? The value in code, or the value in the configuration?
davidfowl commented 2 months ago

Don't we need a string name parameter?

Done.

Should value => defaultValue?

Done.

What happens if I have a configuration value with the name Parameters:? Which wins? The value in code, or the value in the configuration?

Code wins.

eerhardt commented 2 months ago

Code wins.

Hmmm 🤔, maybe it shouldn't be named defaultValue then...

mitchdenny commented 2 months ago

@davidfowl we need to make it so that the callback is async I think.

davidfowl commented 2 months ago

Yes we do, but that's a huge change. @eerhardt attempted did that initially and backed it out. Time to go again 😄

mitchdenny commented 1 month ago

Moved this to backlog. We should make the case for pulling it in to 8.2. My thinking here is that right now people can do things like leverage the config system to allow injecting these values and the config system is extensible. A lot of this is just surfacing API at the main method level.

los93sol commented 1 month ago

I’m working on Hashicorp Vault integration and need something similar. In my scenario Vault is initialized after the container is created and there’s values I need to persist for future runs like unseal keys and root token that aren’t possible to know before the container is initialized. The current user secrets setup only allows for generated values as well and prevents me from being able to provide the value to be persisted so I’m unclear how to proceed and could use some assistance. It seems like a new ParameterDefault type and extension method in ParameterResourceExtensions may be necessary, but I also don’t really understand how this is expected to work when deployed.

mitchdenny commented 3 weeks ago

@los93sol do you have a repo where you are working on this somewhere.

I'm curious about what you are are saying about values you can't know before the container is initialized. Presently we don't have a mechanism for extracting data from the container (unless the hosting extension calls an API on the container process itself).

So this may not necessarily be the droid you are looking for in that case. But I'd like to know more to better understand the challenge you have.

davidfowl commented 1 hour ago

@davidebbo can you look at this one?