| File: RadiusInfrastructure.cs | Web Access |
| Project: src\src\Aspire.Hosting.Radius\Aspire.Hosting.Radius.csproj (Aspire.Hosting.Radius) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable ASPIREPIPELINES001 using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Pipelines; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Aspire.Hosting.Radius; /// <summary> /// Pipeline-step helper invoked by the per-environment /// <c>prepare-deployment-targets-{name}</c> step. Materializes /// <see cref="DeploymentTargetAnnotation"/> instances on compute resources /// scoped to a specific <see cref="RadiusEnvironmentResource"/>. /// </summary> /// <remarks> /// <para> /// This mirrors the K8s/Docker compute-environment integrations /// (see <c>KubernetesEnvironmentResource.PrepareDeploymentTargetsAsync</c> /// and <c>DockerComposeEnvironmentResource.PrepareDeploymentTargetsAsync</c>): /// the step <c>DependsOn(ValidateComputeEnvironments)</c> and /// <c>RequiredBy(BeforeStart)</c>, so ambiguity across multiple compute /// environments is rejected by the framework before this code runs (see /// <c>DistributedApplicationPipeline.ValidateComputeEnvironmentBindings</c>). /// </para> /// <para> /// In <c>Run</c> mode the prepare step is a no-op so the integration adds /// no inner-loop overhead — matching K8s, which doesn't even register its /// environment resource in run mode. /// </para> /// </remarks> internal static class RadiusInfrastructure { /// <summary> /// Materializes <see cref="DeploymentTargetAnnotation"/> instances on every /// compute resource whose target compute environment is <paramref name="environment"/> /// (or its <see cref="RadiusEnvironmentResource.OwningComputeEnvironment"/> parent). /// Resources targeted to a different environment are left untouched so that /// sibling environments (e.g. another Radius env, K8s, Docker Compose) can /// claim them via their own prepare step. /// </summary> internal static Task PrepareDeploymentTargetsAsync( RadiusEnvironmentResource environment, PipelineStepContext context) { // No work to do in Run mode. The environment resource is not registered with // the application builder in Run mode either (see AddRadiusEnvironment), but // guard here regardless in case a caller invokes the prepare step directly // for a manually-constructed environment. if (context.ExecutionContext.IsRunMode) { return Task.CompletedTask; } var logger = context.Services.GetRequiredService<ILoggerFactory>() .CreateLogger(typeof(RadiusInfrastructure).FullName!); logger.LogInformation( "Preparing deployment targets for Radius environment '{EnvironmentName}' in namespace '{Namespace}'", environment.Name, environment.Namespace); // When this environment is a child of a parent compute environment (the AKS-on-K8s // pattern, see KubernetesEnvironmentResource.cs:130-140), resources may target the // parent rather than us directly. Match either. var targetComputeEnvironment = environment.OwningComputeEnvironment ?? environment; foreach (var resource in context.Model.GetComputeResources()) { var resourceComputeEnvironment = resource.GetComputeEnvironment(); // Skip resources that are explicitly targeted to a different compute environment. // ValidateComputeEnvironments (a DependsOn for this step) already fails-fast on // untargeted resources when multiple compute environments exist, so a null value // here means there's exactly one compute environment in the model — which is us. if (resourceComputeEnvironment is not null && resourceComputeEnvironment != environment && resourceComputeEnvironment != environment.OwningComputeEnvironment) { continue; } // Record the annotation against the resource's own compute environment when it is // explicitly bound (via WithComputeEnvironment), falling back to this environment // (or its owning parent) otherwise. ResourceExtensions.GetDeploymentTargetAnnotation // matches the DeploymentTargetAnnotation by the resource's ComputeEnvironmentAnnotation, // so using the parent here would cause an explicitly-targeted resource to look // untargeted. This mirrors KubernetesEnvironmentResource's computeEnvForAnnotation. var computeEnvForAnnotation = resourceComputeEnvironment ?? targetComputeEnvironment; // Skip if a target annotation for this environment already exists. Prepare steps // are idempotent so re-execution (e.g. during test composition) does not duplicate. var alreadyTargeted = resource.Annotations .OfType<DeploymentTargetAnnotation>() .Any(a => a.ComputeEnvironment == computeEnvForAnnotation); if (alreadyTargeted) { continue; } // ContainerRegistry is intentionally left null in this prototype: the integration // does not yet build/push images, so the standard push-prereq chain has no work // to do. When project-publish support lands (see README "Limitations") this // should read from a registry annotation on the environment, the same way // KubernetesEnvironmentResource.GetContainerRegistry does. resource.Annotations.Add(new DeploymentTargetAnnotation(environment) { ComputeEnvironment = computeEnvForAnnotation }); logger.LogDebug( "Attached deployment target annotation to resource '{ResourceName}' targeting Radius environment '{EnvironmentName}'", resource.Name, environment.Name); } return Task.CompletedTask; } }