-
Notifications
You must be signed in to change notification settings - Fork 564
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
On Demand Startup of Resources #5851
Comments
CRUD interface to manage resources at runtime from application code is really needed. We are working on software which deployes services (from docker images) as our users request them. The ablity to implement this with Aspire for local development would be awesome. |
Played with this a little based on what works today: using Microsoft.Extensions.DependencyInjection;
var builder = DistributedApplication.CreateBuilder(args);
builder.AddContainer("redis", "redis").WithExplicitStart();
builder.Build().Run();
public static class ExplicitStartupExtensions
{
public static IResourceBuilder<T> WithExplicitStart<T>(this IResourceBuilder<T> builder)
where T : IResource
{
builder.ApplicationBuilder.Eventing.Subscribe<BeforeResourceStartedEvent>(builder.Resource, async (evt, ct) =>
{
var rns = evt.Services.GetRequiredService<ResourceNotificationService>();
// This is possibly the last safe place to update the resource's annotations
// we need to do it this late because the built in lifecycle annotations are added *very* late
var startCommand = evt.Resource.Annotations.OfType<ResourceCommandAnnotation>().FirstOrDefault(c => c.Type == "resource-start");
if (startCommand is null)
{
return;
}
evt.Resource.Annotations.Remove(startCommand);
// This will block the resource from starting until the "resource-start" command is executed
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
// Create a new command that clones the start command
var newCommand = new ResourceCommandAnnotation(
startCommand.Type,
startCommand.DisplayName,
context =>
{
if (!tcs.Task.IsCompleted)
{
return ResourceCommandState.Enabled;
}
return startCommand.UpdateState(context);
},
context =>
{
if (!tcs.Task.IsCompleted)
{
tcs.SetResult();
return Task.FromResult(CommandResults.Success());
}
return startCommand.ExecuteCommand(context);
},
startCommand.DisplayDescription,
startCommand.Parameter,
startCommand.ConfirmationMessage,
startCommand.IconName,
startCommand.IconVariant,
startCommand.IsHighlighted);
evt.Resource.Annotations.Add(newCommand);
await rns.PublishUpdateAsync(evt.Resource, s => s with { State = new(KnownResourceStates.Waiting, KnownResourceStateStyles.Info) });
await tcs.Task.WaitAsync(ct);
});
return builder;
}
} |
Thank you for the insight, but it's not quite what I have in mind. I can envision an interface to manage Aspire resources, with its instance accessible from a running project in Aspire.
|
Sorry, I wasn't replying to your request for an api to dynamically add resources, I think that's a different, harder and longer term feature request. The other features in this issue are quite doable with existing APIs. |
I had the same cronjob situation as @afscrome (Thank you for sending the issue) and used the suggested solution. public static class ExplicitStartupExtensions
{
public static IResourceBuilder<T> WithExplicitStart<T>(this IResourceBuilder<T> builder)
where T : IResource
{
builder.ApplicationBuilder.Eventing.Subscribe<BeforeResourceStartedEvent>(builder.Resource, async (evt, ct) =>
{
var rns = evt.Services.GetRequiredService<ResourceNotificationService>();
// This is possibly the last safe place to update the resource's annotations
// we need to do it this late because the built in lifecycle annotations are added *very* late
var startCommand = evt.Resource.Annotations.OfType<ResourceCommandAnnotation>().FirstOrDefault(c => c.Name == "resource-start");
if (startCommand is null)
{
return;
}
evt.Resource.Annotations.Remove(startCommand);
// This will block the resource from starting until the "resource-start" command is executed
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
// Create a new command that clones the start command
var newCommand = new ResourceCommandAnnotation(
startCommand.Name,
startCommand.DisplayName,
context => !tcs.Task.IsCompleted ? ResourceCommandState.Enabled : startCommand.UpdateState(context),
context =>
{
if (tcs.Task.IsCompleted)
return startCommand.ExecuteCommand(context);
tcs.SetResult();
return Task.FromResult(CommandResults.Success());
},
startCommand.DisplayDescription,
startCommand.Parameter,
startCommand.ConfirmationMessage,
startCommand.IconName,
startCommand.IconVariant,
startCommand.IsHighlighted);
evt.Resource.Annotations.Add(newCommand);
await rns.PublishUpdateAsync(evt.Resource, s => s with { State = new ResourceStateSnapshot(KnownResourceStates.Waiting, KnownResourceStateStyles.Info) });
await tcs.Task.WaitAsync(ct);
});
return builder;
}
} |
I think a way to mark a resource as not automatically starting is a good place to begin here. @davidfowl Questions:
|
@karolz-ms Do you have thoughts here about resources that don't startup automatically? |
Either that or something like that means don't start.
And azure resources, so it'll be an annotation that a resource could choose to implement.
That's probably clearest, but Stopped might be ok.
Yep. That works.
Unclear if you want to resolve environment variables and raise events before the user starts. I think the resource should be completely cold and nothing will run until you start it. |
I'd love it if there was a way to programmatically start such resources. For example, the following could be used to run database migrations when the database becomes ready. var sql = builder.AddSqlServer("sql")
.WithLifetime(ContainerLifetime.Persistent)
;
var db = sql.AddDatabase("mydb");
var migrator = builder.AddExecutable("db-migrations", "dotnet-ef", "..")
.WithArgs("database",
"update",
"--connection", db.Resource.ConnectionStringExpression,
"--project", new AspireInitialisation_DbMigrations().ProjectPath,
"--no-build",
"-v")
.WithExplicitStart() // Or whatever API is deccided
;
builder.Eventing.Subscribe<ResourceReadyEvent>(async (evt, ct) =>
{
var rns = evt.Services.GetRequiredService<ResourceNotificationService>();
migrator.Start(); // or some API for equalivent functionality.
await rns.WaitForResourceAsync(evt.Resource.Name, KnownResourceStates.Finished, ct);
}); |
@afscrome I appreciate that you’re find creative ways to accomplish your scenarios given what works today. I’d encourage you to file issues / discussions with the full end to end experiences that you’re building for your team so we can figure out the best approach and building blocks required |
I am wondering if this is the exact same situation for implementing it as described in this issue, but the scenario is very similar: I'd love to manually start support-components like PgAdmin or Redis Commander. var postgresql = builder.AddPostgres("Postgresql")
.WithPgAdmin(c => c.WithExplicitStart());
var cache = builder.AddRedis("Cache")
.WithRedisCommander(c => c.WithExplicitStart());
var mailDev = builder.AddContainer("Maildev", "maildev/maildev").WithExplicitStart(); They are great when I need them and I love the Aspire experience, but I only need them for some tasks. |
This is done in 9.1 #7324 |
Is there an existing issue for this?
Is your feature request related to a problem? Please describe the problem.
With the addition of being able to start/stop resources in the dashboard, it would be useful to be able to support resources which are started on demand rather than automatically on startup. A few use cases I can think of:
CronJob
resources deployed to Kubernetes - We have a few resources we deploy as nightly cron jobs to kuberentes. In local dev, it would be helpful to have these availableDescribe the solution you'd like
Some kind of way to mark a resource so that Aspire doesn't automatically start the resource, instead waiting for the user to click the start button in the UI.
An alternative approach would be to provide a way for commands to dynamically add new resources, which would allow you to use a command to spawn up a new resource. This work well for options 2 & 3 above, but doesn't fit 1 so well.
Additional context
No response
The text was updated successfully, but these errors were encountered: