dotnet / aspire

Tools, templates, and packages to accelerate building observable, production-ready apps
https://learn.microsoft.com/dotnet/aspire
MIT License
3.87k stars 464 forks source link

Orchestrator & Operators #6040

Open mitchdenny opened 1 month ago

mitchdenny commented 1 month ago

Since .NET Aspire 8.0 the start-up of resources has occurred during the AppHost start-up process blocking the return of StartAsync(...) until all resources are running. This is because as part of the startup process components like ApplicationExecutor need to block startup of particular resources until environment variables can be fully resolved and with the addition of WaitFor in .NET Aspire 9.0 - until the health checks associated with the resource return healthy.

Additionally, we have inconsistent behavior between resources that are handled by specific code (such as containers, processes, projects in ApplicationExecutor and Azure resources via Azure Provisioner) and custom resources that sit inside the application model in their default starting state.

At the moment if you are implementing your own custom resource you need to create a lifecycle hook or wireup to an event to trigger a background process to handle doing things like publishing events and updating status via the ResourceNotificationService.

This proposal recommends introducing a primitive in the the .NET Aspire application model called an operator which is a component which is given responsibility for managing a resource. ApplicationExecutor and AzureProvisioner are examples of operators but are implemented in different ways.

The goal would be to introduce a mechanism where when a resource is added to the application model an operator is assigned based on a set of rules (or can be explicitly specified in annotations). From that point forward that operator is fully responsible for managing the lifecycle for that resource.

As part of this change, we would move from a blocking process in startup to a model where operators start executing after the application has started.

In turn this change would open up the possibility of the application model being more mutable during the app hosts lifetime. With the introduction of command annotations, we have the ability to trigger code execution from the dashboard. Often this code can just be imperative C# code - however in some cases it would be useful to be able to launch a container resource or a process as a way of fulfilling the requirements of a command being executed. By making the application model mutable at runtime (e.g. adding resources after startup) we may be able to facilitate this.

A scenario might be having a load test command on a API project which triggers an associated k6 load test inside a container image and being able to view the outputs of the test.

I see operators as being singletons which are registered in the container. There would be a single operator for container resources, one for executables, one for projects. All of these would probably delegate a lot of their processing to the application executor which would need work to enable it to be less "single shot" - some of this has already been done with the introduction of start/stop for resources.

There would be a single "orchestrator" resource who is responsible for selecting the most appropriate operator for a given resource. This would be a cooperative process where each operator is given an opportunity to nominate itself for a specific resource. If more than one operator says it can handle a resource then we would need some mechanism to pin a resource to a particular operator - this could be an annotation.

var builder = DistributedApplication.CreateBuilder(args);
var myresource = builder.AddMyResource("myresource")
                        .WithOperator<CustomResourceOperator>();

Operators would be able to "hand-off" resources to another operator. The example I'm considering is something like an Azure resource where an emulator is being used. The Azure resource operator would be assigned the resource, but because it has container annotations it would call the container resource operator and get it to "own" the resource.

mitchdenny commented 1 month ago

/cc @davidfowl @JamesNK @eerhardt @drewnoakes @sebastienros