Is there a way to a resource to tell the apphost to start another resource? #16432
|
I have an asp.net resource in azure container apps and a job resource in azure container app jobs. My current hack involves the use of ServiceBus, but that is a pain because it makes the apphost take about 10x longer to start. |
Replies: 2 comments 4 replies
|
The cleanest local approach is to expose a minimal HTTP trigger inside the AppHost itself using In var job = builder.AddProject<Projects.MyJob>("my-job")
.WithExplicitStart(); // don't start automatically
var api = builder.AddProject<Projects.MyApi>("my-api")
.WithEnvironment("JobTriggerUrl", ???); // see belowUnfortunately the AppHost doesn't natively expose a custom HTTP endpoint you can call from a resource today. The practical workaround without ServiceBus: Option 1 - Aspire Dashboard "Start" via Add a thin controller to your ASP.NET project that acts as the "job trigger" locally. Detect the environment (e.g. Option 2 - Use Aspire 9+ supports job.WithCommand(
name: "trigger",
displayName: "Trigger Job",
executeCommand: async context =>
{
// use IResourceNotificationService or process start here
return CommandResults.Success();
});This won't let your ASP.NET app call it programmatically, but it's a zero-overhead alternative to ServiceBus if manual triggering is acceptable in dev. Option 3 - Lightweight local trigger endpoint in a separate minimal API Add a tiny There's no first-class API today for one resource to tell the AppHost to start another programmatically at runtime. It may be worth opening a feature request for |
|
James Duley (@parched) There are a couple of ways of doing this. Artpupser (@Artpupser) has suggested a couple of possibilities but I have one where I have an explicit package designed to help with this kind of thing. Using C3D.Extensions.Aspire.OutputWatcher I did a quick proof of concept for you at CZEMacLeod/JobTriggerApp. This version uses a The main crux of the solution is the apphost and the dev mode service implementation. using Microsoft.Extensions.DependencyInjection;
var builder = DistributedApplication.CreateBuilder(args);
var magicString = Guid.NewGuid().ToString(); // Some magic string which will be unique and never appear in the logs unless it's from our trigger app. This is used to know for sure that the log message we are watching for is from our trigger app and not some other log message that coincidentally looks like it could be a trigger message. By using a unique magic string, we can be confident that when we see this string in the logs, it's a signal from our trigger app to start the job.
var magicParameter = builder.AddParameter("jobMagicString", magicString);
var job = builder.AddProject<Projects.JobApp>("job") // This can be any kind of resource that aspire can run
.WithExplicitStart(); // The job doesn't run unless we manually start it, either from the dashboard, or in our case, from the trigger app when it sees the magic string in the logs.
var worker = builder.AddProject<Projects.TriggerApp>("trigger") // This is the trigger app that we will watch the logs for the magic string and start the job when it sees it. This can be any kind of resource that can run code and log output in azure. In this case it is a console app using HostApplicationBuilder.
.WithEnvironment("DOTNET_ENVIRONMENT", "Development") // Ensure the trigger app runs in development environment to use JobStarterDev which logs the magic string
.WithEnvironment("DOTNET_JobTrigger_MagicString", magicParameter) // Pass the magic string in as an environment variable
.WithOutputWatcher(msg => msg.EndsWith(magicString, StringComparison.InvariantCulture)) // The logged message ends with the magic string, so we can watch for it to know when to start the job
.OnMatched(async (o, c) =>
{
var rcs = o.ServiceProvider.GetRequiredService<ResourceCommandService>(); // We get the ResourceCommandService from the service provider, which allows us to execute commands on resources in our projects.
await rcs.ExecuteCommandAsync(job.Resource, "resource-start", c); // When we see the magic string in the logs, we execute the "resource-start" command on the job resource, which starts the job.
})
;
builder.Build().Run(); internal class JobStarterDev : IJobStarter
{
private readonly ILogger<JobStarterDev> logger;
private readonly IConfiguration configuration;
private readonly string magicString;
public JobStarterDev(ILogger<JobStarterDev> logger, IConfiguration configuration)
{
this.logger = logger;
this.configuration = configuration;
this.magicString = configuration["DOTNET_JobTrigger_MagicString"] ?? "No Magic String Found";
}
public Task StartJobAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Starting Job in Development Environment using aspire host. {magicString}", magicString);
return Task.CompletedTask;
}
}I hope this helps! |
James Duley (@parched) There are a couple of ways of doing this. Artpupser (@Artpupser) has suggested a couple of possibilities but I have one where I have an explicit package designed to help with this kind of thing.
Using C3D.Extensions.Aspire.OutputWatcher
you can monitor the logged output of a resource and perform actions when a specific string is detected.
There is an example in the repo where it using a console app to do some work and then pass the logged results into another resource as an environment variable.
I did a quick proof of concept for you at CZEMacLeod/JobTriggerApp.
This version uses a
magicString(just a guid) that is known by the aspire host and passed to the main ap…