From 0bebe988814a176a2b8f1a867a9a750657f15126 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:58:25 +0000 Subject: [PATCH 1/5] Initial plan From 6245b34e20e774006a6f3311b7f7e3c1d0b191cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:11:17 +0000 Subject: [PATCH 2/5] Initial plan for AOT compatibility Agent-Logs-Url: https://github.com/dotnet/yarp/sessions/b1d776b4-b2b5-4a50-b364-4aec6b74df35 Co-authored-by: eerhardt <8291187+eerhardt@users.noreply.github.com> --- global.json | 6 ++++-- global.json.bak | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 global.json.bak diff --git a/global.json b/global.json index 67669b915..d04e4666b 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,11 @@ { "sdk": { - "version": "11.0.100-preview.3.26170.106" + "version": "9.0.312", + "rollForward": "latestMajor", + "allowPrerelease": true }, "tools": { - "dotnet": "11.0.100-preview.3.26170.106", + "dotnet": "9.0.312", "runtimes": { "dotnet": [ "8.0.13", diff --git a/global.json.bak b/global.json.bak new file mode 100644 index 000000000..67669b915 --- /dev/null +++ b/global.json.bak @@ -0,0 +1,25 @@ +{ + "sdk": { + "version": "11.0.100-preview.3.26170.106" + }, + "tools": { + "dotnet": "11.0.100-preview.3.26170.106", + "runtimes": { + "dotnet": [ + "8.0.13", + "9.0.2" + ], + "aspnetcore": [ + "8.0.13", + "9.0.2" + ] + } + }, + "test": { + "runner": "Microsoft.Testing.Platform" + }, + "msbuild-sdks": { + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26204.1", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26204.1" + } +} From ad10145d045fd4e6077e56847f6500c0e19da90d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:18:13 +0000 Subject: [PATCH 3/5] Enable AOT compatibility for Yarp.Kubernetes.Controller - Use JsonStringEnumConverter (generic) in Message.cs for source generator compatibility - Create KubernetesJsonSerializerContext with JsonSerializerContext for AOT-safe JSON serialization - Update DispatchConfigProvider, DispatchActionResult, Receiver to use source-generated JsonSerializer methods - Update Reconciler to use source-generated serialization for logging - Update KubernetesReverseProxyServiceCollectionExtensions to use ConfigurationBinder source generator-compatible Bind call Agent-Logs-Url: https://github.com/dotnet/yarp/sessions/b1d776b4-b2b5-4a50-b364-4aec6b74df35 Co-authored-by: eerhardt <8291187+eerhardt@users.noreply.github.com> --- global.json | 6 ++--- global.json.bak | 25 ------------------- ...ReverseProxyServiceCollectionExtensions.cs | 2 +- .../Protocol/DispatchActionResult.cs | 3 ++- .../Protocol/DispatchConfigProvider.cs | 2 +- .../KubernetesJsonSerializerContext.cs | 16 ++++++++++++ src/Kubernetes.Controller/Protocol/Message.cs | 2 +- .../Protocol/Receiver.cs | 3 ++- .../Services/Reconciler.cs | 5 ++-- 9 files changed, 28 insertions(+), 36 deletions(-) delete mode 100644 global.json.bak create mode 100644 src/Kubernetes.Controller/Protocol/KubernetesJsonSerializerContext.cs diff --git a/global.json b/global.json index d04e4666b..67669b915 100644 --- a/global.json +++ b/global.json @@ -1,11 +1,9 @@ { "sdk": { - "version": "9.0.312", - "rollForward": "latestMajor", - "allowPrerelease": true + "version": "11.0.100-preview.3.26170.106" }, "tools": { - "dotnet": "9.0.312", + "dotnet": "11.0.100-preview.3.26170.106", "runtimes": { "dotnet": [ "8.0.13", diff --git a/global.json.bak b/global.json.bak deleted file mode 100644 index 67669b915..000000000 --- a/global.json.bak +++ /dev/null @@ -1,25 +0,0 @@ -{ - "sdk": { - "version": "11.0.100-preview.3.26170.106" - }, - "tools": { - "dotnet": "11.0.100-preview.3.26170.106", - "runtimes": { - "dotnet": [ - "8.0.13", - "9.0.2" - ], - "aspnetcore": [ - "8.0.13", - "9.0.2" - ] - } - }, - "test": { - "runner": "Microsoft.Testing.Platform" - }, - "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26204.1", - "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26204.1" - } -} diff --git a/src/Kubernetes.Controller/Management/KubernetesReverseProxyServiceCollectionExtensions.cs b/src/Kubernetes.Controller/Management/KubernetesReverseProxyServiceCollectionExtensions.cs index 663fa5921..521d6db16 100644 --- a/src/Kubernetes.Controller/Management/KubernetesReverseProxyServiceCollectionExtensions.cs +++ b/src/Kubernetes.Controller/Management/KubernetesReverseProxyServiceCollectionExtensions.cs @@ -80,7 +80,7 @@ public static IServiceCollection AddKubernetesControllerRuntime(this IServiceCol services.AddHostedService(); services.AddSingleton(); services.AddTransient(); - services.Configure(config.GetSection("Yarp")); + services.Configure(o => config.GetSection("Yarp").Bind(o)); // Register the necessary Kubernetes resource informers services.RegisterResourceInformer(); diff --git a/src/Kubernetes.Controller/Protocol/DispatchActionResult.cs b/src/Kubernetes.Controller/Protocol/DispatchActionResult.cs index 771b182a1..ff5d339e7 100644 --- a/src/Kubernetes.Controller/Protocol/DispatchActionResult.cs +++ b/src/Kubernetes.Controller/Protocol/DispatchActionResult.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Yarp.Kubernetes.Controller.Protocol; using Yarp.Kubernetes.Protocol; namespace Yarp.Kubernetes.Controller.Dispatching; @@ -54,7 +55,7 @@ public async Task ExecuteResultAsync(ActionContext context) var utf8Bytes = JsonSerializer.SerializeToUtf8Bytes(new Message { MessageType = MessageType.Heartbeat - }); + }, KubernetesJsonSerializerContext.Default.Message); while (!cancellationToken.IsCancellationRequested) { diff --git a/src/Kubernetes.Controller/Protocol/DispatchConfigProvider.cs b/src/Kubernetes.Controller/Protocol/DispatchConfigProvider.cs index 000c8e403..566122f51 100644 --- a/src/Kubernetes.Controller/Protocol/DispatchConfigProvider.cs +++ b/src/Kubernetes.Controller/Protocol/DispatchConfigProvider.cs @@ -34,7 +34,7 @@ public async Task UpdateAsync(IReadOnlyList routes, IReadOnlyList))] +[JsonSerializable(typeof(List))] +internal sealed partial class KubernetesJsonSerializerContext : JsonSerializerContext +{ +} diff --git a/src/Kubernetes.Controller/Protocol/Message.cs b/src/Kubernetes.Controller/Protocol/Message.cs index 4bdaddd91..8b6245afd 100644 --- a/src/Kubernetes.Controller/Protocol/Message.cs +++ b/src/Kubernetes.Controller/Protocol/Message.cs @@ -16,7 +16,7 @@ public enum MessageType public struct Message { - [JsonConverter(typeof(JsonStringEnumConverter))] + [JsonConverter(typeof(JsonStringEnumConverter))] public MessageType MessageType { get; set; } public string Key { get; set; } diff --git a/src/Kubernetes.Controller/Protocol/Receiver.cs b/src/Kubernetes.Controller/Protocol/Receiver.cs index 46830002a..b5f48858b 100644 --- a/src/Kubernetes.Controller/Protocol/Receiver.cs +++ b/src/Kubernetes.Controller/Protocol/Receiver.cs @@ -12,6 +12,7 @@ using Microsoft.Extensions.Options; using Yarp.Kubernetes.Controller.Configuration; using Yarp.Kubernetes.Controller.Hosting; +using Yarp.Kubernetes.Controller.Protocol; using Yarp.Kubernetes.Controller.Rate; namespace Yarp.Kubernetes.Protocol; @@ -65,7 +66,7 @@ public override async Task RunAsync(CancellationToken cancellationToken) break; } - var message = System.Text.Json.JsonSerializer.Deserialize(json); + var message = System.Text.Json.JsonSerializer.Deserialize(json, KubernetesJsonSerializerContext.Default.Message); Logger.LogInformation("Received {MessageType} for {MessageKey}", message.MessageType, message.Key); Logger.LogInformation(json); diff --git a/src/Kubernetes.Controller/Services/Reconciler.cs b/src/Kubernetes.Controller/Services/Reconciler.cs index a5302537f..c2733062c 100644 --- a/src/Kubernetes.Controller/Services/Reconciler.cs +++ b/src/Kubernetes.Controller/Services/Reconciler.cs @@ -11,6 +11,7 @@ using Yarp.Kubernetes.Controller.Client; using Yarp.Kubernetes.Controller.Configuration; using Yarp.Kubernetes.Controller.Converters; +using Yarp.Kubernetes.Controller.Protocol; namespace Yarp.Kubernetes.Controller.Services; @@ -63,8 +64,8 @@ public async Task ProcessAsync(CancellationToken cancellationToken) var clusters = configContext.BuildClusterConfig(); - _logger.LogInformation(JsonSerializer.Serialize(configContext.Routes)); - _logger.LogInformation(JsonSerializer.Serialize(clusters)); + _logger.LogInformation(JsonSerializer.Serialize(configContext.Routes, KubernetesJsonSerializerContext.Default.ListRouteConfig)); + _logger.LogInformation(JsonSerializer.Serialize(clusters, KubernetesJsonSerializerContext.Default.ListClusterConfig)); await _updateConfig.UpdateAsync(configContext.Routes, clusters, cancellationToken).ConfigureAwait(false); await _ingressResourceStatusUpdater.UpdateStatusAsync(cancellationToken); From dd0c957cb4f42b0fc77fe303a15f0f3ef4648cf7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:47:16 +0000 Subject: [PATCH 4/5] Add AOT compatibility test app for Yarp.Kubernetes.Controller - Add IsAotCompatible=true to Yarp.Kubernetes.Controller.csproj to enable build-time trim/AOT analysis - Create testassets/Kubernetes.AotCompatibility.TestApp exercising the three main usage scenarios: combined (AddKubernetesReverseProxy), monitor (AddKubernetesIngressMonitor + AddKubernetesDispatchController), and receiver (Configure + Receiver + LoadFromMessages) - Add test app to YARP.slnx solution Agent-Logs-Url: https://github.com/dotnet/yarp/sessions/24caf1e2-cfc2-4e24-ae5b-76759b3c709c Co-authored-by: eerhardt <8291187+eerhardt@users.noreply.github.com> --- YARP.slnx | 1 + .../Yarp.Kubernetes.Controller.csproj | 1 + .../Program.cs | 35 +++++++++++++++++++ ...Kubernetes.AotCompatibility.TestApp.csproj | 14 ++++++++ 4 files changed, 51 insertions(+) create mode 100644 testassets/Kubernetes.AotCompatibility.TestApp/Program.cs create mode 100644 testassets/Kubernetes.AotCompatibility.TestApp/Yarp.Kubernetes.AotCompatibility.TestApp.csproj diff --git a/YARP.slnx b/YARP.slnx index 8038a9727..c2535b84c 100644 --- a/YARP.slnx +++ b/YARP.slnx @@ -54,6 +54,7 @@ + diff --git a/src/Kubernetes.Controller/Yarp.Kubernetes.Controller.csproj b/src/Kubernetes.Controller/Yarp.Kubernetes.Controller.csproj index baae88ecf..c39c7ca17 100644 --- a/src/Kubernetes.Controller/Yarp.Kubernetes.Controller.csproj +++ b/src/Kubernetes.Controller/Yarp.Kubernetes.Controller.csproj @@ -7,6 +7,7 @@ $(NoWarn);CS8002 true false + true yarp;dotnet;reverse-proxy;aspnetcore;kubernetes diff --git a/testassets/Kubernetes.AotCompatibility.TestApp/Program.cs b/testassets/Kubernetes.AotCompatibility.TestApp/Program.cs new file mode 100644 index 000000000..6771b482e --- /dev/null +++ b/testassets/Kubernetes.AotCompatibility.TestApp/Program.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This app is used to test AOT compatibility of Yarp.Kubernetes.Controller. +// It exercises the main public API surface to ensure no AOT/trimming warnings +// are emitted at build or publish time. + +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Yarp.Kubernetes.Protocol; + +// Combined controller scenario (ingress controller + YARP reverse proxy) +{ + var builder = WebApplication.CreateBuilder(args); + builder.WebHost.UseKubernetesReverseProxyCertificateSelector(); + builder.Services.AddKubernetesReverseProxy(builder.Configuration); + _ = builder.Build(); +} + +// Monitor-only scenario (ingress monitor + dispatch controller) +{ + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddKubernetesIngressMonitor(builder.Configuration); + builder.Services.AddControllers().AddKubernetesDispatchController(); + _ = builder.Build(); +} + +// Receiver scenario (side-car that receives config from ingress monitor) +{ + var builder = WebApplication.CreateBuilder(args); + builder.Services.Configure(builder.Configuration.Bind); + builder.Services.AddHostedService(); + builder.Services.AddReverseProxy().LoadFromMessages(); + _ = builder.Build(); +} diff --git a/testassets/Kubernetes.AotCompatibility.TestApp/Yarp.Kubernetes.AotCompatibility.TestApp.csproj b/testassets/Kubernetes.AotCompatibility.TestApp/Yarp.Kubernetes.AotCompatibility.TestApp.csproj new file mode 100644 index 000000000..eb79efa29 --- /dev/null +++ b/testassets/Kubernetes.AotCompatibility.TestApp/Yarp.Kubernetes.AotCompatibility.TestApp.csproj @@ -0,0 +1,14 @@ + + + + $(ReleaseTFMs) + Exe + true + enable + + + + + + + From b5bf7f6eedba6e2b6efb6dad4f9308c5502506dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:00:40 +0000 Subject: [PATCH 5/5] Update AOT test app: net10.0 TFM, all AOT-compatible project refs, and TrimmerRootAssembly - Change TargetFrameworks to net10.0 (single TFM as requested) - Add project references for all three AOT-compatible YARP assemblies: Yarp.Kubernetes.Controller, Yarp.ReverseProxy, Yarp.Telemetry.Consumption - Add TrimmerRootAssembly items for each assembly to ensure trimmer roots them - Extend Program.cs with ReverseProxy and TelemetryConsumption usage scenarios Agent-Logs-Url: https://github.com/dotnet/yarp/sessions/9f2472df-913f-4feb-82f7-9e5b65f7d1c3 Co-authored-by: eerhardt <8291187+eerhardt@users.noreply.github.com> --- .../Program.cs | 17 ++++++++++++++++- ...p.Kubernetes.AotCompatibility.TestApp.csproj | 10 +++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/testassets/Kubernetes.AotCompatibility.TestApp/Program.cs b/testassets/Kubernetes.AotCompatibility.TestApp/Program.cs index 6771b482e..1bce089e2 100644 --- a/testassets/Kubernetes.AotCompatibility.TestApp/Program.cs +++ b/testassets/Kubernetes.AotCompatibility.TestApp/Program.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// This app is used to test AOT compatibility of Yarp.Kubernetes.Controller. +// This app is used to test AOT compatibility of all AOT-compatible YARP assemblies. // It exercises the main public API surface to ensure no AOT/trimming warnings // are emitted at build or publish time. @@ -33,3 +33,18 @@ builder.Services.AddReverseProxy().LoadFromMessages(); _ = builder.Build(); } + +// ReverseProxy standalone scenario +{ + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddReverseProxy() + .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); + _ = builder.Build(); +} + +// TelemetryConsumption scenario +{ + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddTelemetryListeners(); + _ = builder.Build(); +} diff --git a/testassets/Kubernetes.AotCompatibility.TestApp/Yarp.Kubernetes.AotCompatibility.TestApp.csproj b/testassets/Kubernetes.AotCompatibility.TestApp/Yarp.Kubernetes.AotCompatibility.TestApp.csproj index eb79efa29..c93ea72d2 100644 --- a/testassets/Kubernetes.AotCompatibility.TestApp/Yarp.Kubernetes.AotCompatibility.TestApp.csproj +++ b/testassets/Kubernetes.AotCompatibility.TestApp/Yarp.Kubernetes.AotCompatibility.TestApp.csproj @@ -1,7 +1,7 @@ - $(ReleaseTFMs) + net10.0 Exe true enable @@ -9,6 +9,14 @@ + + + + + + + +