Skip to content

Commit dc5f776

Browse files
authored
Add a Json schema for YARP configuration (#2758)
* Add initial schema * Cleanup json schema * Update a couple XML docs * Allow null in most places * Add schema test * Extract defaults * Fix casing * Use booleans instead of strings in sample configs * Drop Uri format requirement on addresses * Relax requirement for HttpMethodChange Set * Make the schema more strict * Improve descriptions * Validate all sample configuration in unit test * Drop minus sign from duration patterns
1 parent 1b81bb5 commit dc5f776

File tree

13 files changed

+1619
-245
lines changed

13 files changed

+1619
-245
lines changed

YARP.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
99
global.json = global.json
1010
NuGet.config = NuGet.config
1111
TFMs.props = TFMs.props
12+
eng\Versions.props = eng\Versions.props
1213
EndProjectSection
1314
EndProject
1415
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yarp.ReverseProxy", "src\ReverseProxy\Yarp.ReverseProxy.csproj", "{568EF8AE-7624-490D-A19F-C25D076FF091}"

eng/Versions.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<SerilogSinksConsoleVersion>3.1.1</SerilogSinksConsoleVersion>
3333
<YamlDotNetVersion>16.2.1</YamlDotNetVersion>
3434
<KubernetesClientVersion>15.0.1</KubernetesClientVersion>
35+
<JsonSchemaNetVersion>7.0.2</JsonSchemaNetVersion>
3536
<!-- Container app dependencies -->
3637
<YarpNugetVersion>2.2.0</YarpNugetVersion>
3738
<MicrosoftExtensionsServiceDiscovery>8.2.2</MicrosoftExtensionsServiceDiscovery>

samples/ReverseProxy.Config.Sample/appsettings.json

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
// matches /download/* and routes to "allClusterProps"
3131
"ClusterId": "allClusterProps", // Name of one of the clusters
3232
"Order": 0, // Lower numbers have higher precidence, default is 0
33-
"Authorization Policy": "Anonymous", // Name of the policy or "Default", "Anonymous"
33+
"AuthorizationPolicy": "Anonymous", // Name of the policy or "Default", "Anonymous"
3434
"CorsPolicy": "disable", // Name of the CorsPolicy to apply to this route or "default", "disable"
3535
"Match": { // Rules that have to be met for the route to match the request
3636
"Path": "/download/{**remainder}", // The path to match using ASP.NET syntax.
@@ -53,7 +53,7 @@
5353
}
5454
]
5555
},
56-
"MetaData": { // List of key value pairs that can be used by custom extensions
56+
"Metadata": { // List of key value pairs that can be used by custom extensions
5757
"MyName": "MyValue"
5858
},
5959
"Transforms": [ // List of transforms. See ReverseProxy.Transforms.Sample for more details
@@ -80,45 +80,67 @@
8080
},
8181
"another_destination": {
8282
"Address": "https://10.20.30.40",
83-
"Health": "https://10.20.30.40:12345" // override for active health checks
83+
"Health": "https://10.20.30.40:12345", // override for active health checks
84+
"Host": "contoso",
85+
"Metadata": {
86+
"SomeKey": "SomeValue"
87+
}
8488
}
8589
},
8690
"LoadBalancingPolicy": "PowerOfTwoChoices", // Alternatively "First", "Random", "RoundRobin", "LeastRequests"
8791
"SessionAffinity": { // Ensures subsequent requests from a client go to the same destination server
8892
"Enabled": true, // Defaults to 'false'
8993
"Policy": "HashCookie", // Default, alternatively "Cookie" or "CustomHeader"
9094
"FailurePolicy": "Redistribute", // default, alternatively "Return503Error"
91-
"AffinityKeyName": "MySessionCookieName" // Required, no default
95+
"AffinityKeyName": "MySessionCookieName", // Required, no default
96+
"Cookie": { // Options for cookie based session affinity
97+
"Path": "/",
98+
"SameSite": "None",
99+
"HttpOnly": true,
100+
"Expiration": "00:30:00",
101+
"Domain": "example.com",
102+
"MaxAge": "08:00:00",
103+
"SecurePolicy": "Always",
104+
"IsEssential": true
105+
}
92106
},
93107
"HealthCheck": { // Ways to determine which destinations should be filtered out due to unhealthy state
94108
"Active": { // Makes API calls to validate the health of each destination
95-
"Enabled": "true",
109+
"Enabled": true,
96110
"Interval": "00:00:10", // How often to query for health data
97111
"Timeout": "00:00:10", // Timeout for the health check request/response
98112
"Policy": "ConsecutiveFailures", // Or other custom policy that has been registered
99-
"Path": "/favicon.ico" // API endpoint to query for health state. Looks for 2XX response codes to indicate healthy state
113+
"Path": "/favicon.ico", // API endpoint to query for health state. Looks for 2XX response codes to indicate healthy state
100114
// Typically something like "/api/health" but used favicon to enable sample to run
115+
"Query": "?healthCheck=true" // Query string to append to the health check request
101116
},
102117
"Passive": { // Disables destinations based on HTTP response codes for proxy requests
103118
"Enabled": true, // Defaults to false
104119
"Policy": "TransportFailureRate", // Or other custom policy that has been registered
105120
"ReactivationPeriod": "00:00:10" // how long before the destination is re-enabled
106-
}
121+
},
122+
"AvailableDestinationsPolicy": "HealthyOrPanic" // Policy for which destinations can be used when sending requests
107123
},
108124
"HttpClient": { // Configuration of HttpClient instance used to contact destinations
109-
"SSLProtocols": "Tls13",
125+
"SslProtocols": [ "Tls13" ],
110126
"DangerousAcceptAnyServerCertificate": true, // Disables destination cert validation
111127
"MaxConnectionsPerServer": 1024, // Destination server can further limit this number
112128
"EnableMultipleHttp2Connections": true,
113129
"RequestHeaderEncoding": "Latin1", // How to interpret non ASCII characters in proxied request's header values
114-
"ResponseHeaderEncoding": "Latin1" // How to interpret non ASCII characters in proxied request's response header values
130+
"ResponseHeaderEncoding": "Latin1", // How to interpret non ASCII characters in proxied request's response header values
131+
"WebProxy": { // Optinal proxy configuration for outgoing requests
132+
"Address": "127.0.0.1",
133+
"BypassOnLocal": true,
134+
"UseDefaultCredentials": false
135+
}
115136
},
116137
"HttpRequest": { // Options for sending request to destination
117-
"Timeout": "00:02:00", // Timeout for the HttpRequest
138+
"ActivityTimeout": "00:02:00", // Activity timeout for the request
118139
"Version": "2", // Http Version that should be tried first
119-
"VersionPolicy": "RequestVersionOrLower" // Policy for which other versions can be be used
140+
"VersionPolicy": "RequestVersionOrLower", // Policy for which other versions can be be used
141+
"AllowResponseBuffering": false
120142
},
121-
"MetaData": { // Custom Key/value pairs for extensibility
143+
"Metadata": { // Custom Key/value pairs for extensibility
122144
"TransportFailureRateHealthPolicy.RateLimit": "0.5", // Used by Passive health policy
123145
"MyKey": "MyValue"
124146
}

samples/ReverseProxy.Transforms.Sample/appsettings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
},
2727
"Transforms": [
2828
{ "PathPrefix": "/prefix" },
29-
{ "RequestHeadersCopy": "true" },
30-
{ "RequestHeaderOriginalHost": "false" },
29+
{ "RequestHeadersCopy": true },
30+
{ "RequestHeaderOriginalHost": false },
3131
{
3232
"RequestHeader": "foo0",
3333
"Append": "bar"

src/Common/Package.targets

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Project>
2+
<ItemGroup>
3+
<JsonSchemaSegment Include="$(MSBuildThisFileDirectory)..\..\ConfigurationSchema.json"
4+
FilePathPattern="appsettings\..*json" />
5+
</ItemGroup>
6+
</Project>

src/Directory.Build.props

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,29 @@
33
<!-- Recurse up. -->
44
<Import Project="$(MSBuildThisFileDirectory)..\Directory.Build.props" />
55

6+
<!-- Include ConfigurationSchema.json in the package if it exists. -->
7+
<PropertyGroup>
8+
<ConfigurationSchemaPath>$(MSBuildProjectDirectory)\ConfigurationSchema.json</ConfigurationSchemaPath>
9+
<ConfigurationSchemaExists Condition="Exists('$(ConfigurationSchemaPath)')">true</ConfigurationSchemaExists>
10+
</PropertyGroup>
11+
12+
<ItemGroup Condition="'$(ConfigurationSchemaExists)' == 'true'">
13+
<None Include="$(ConfigurationSchemaPath)"
14+
Pack="True"
15+
PackagePath="ConfigurationSchema.json" />
16+
</ItemGroup>
17+
18+
<PropertyGroup Condition="'$(ConfigurationSchemaExists)' == 'true'">
19+
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);AddPackageTargetsInPackage</TargetsForTfmSpecificContentInPackage>
20+
</PropertyGroup>
21+
22+
<Target Name="AddPackageTargetsInPackage">
23+
<ItemGroup>
24+
<TfmSpecificPackageFile Include="$(MSBuildThisFileDirectory)Common\Package.targets"
25+
PackagePath="buildTransitive\$(TargetFramework)\$(PackageId).targets" />
26+
</ItemGroup>
27+
</Target>
28+
629
<PropertyGroup>
730
<IsShipping>true</IsShipping>
831
<IsPackable>true</IsPackable>

src/ReverseProxy/Configuration/PassiveHealthCheckConfig.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using Yarp.ReverseProxy.Model;
56

67
namespace Yarp.ReverseProxy.Configuration;
78

@@ -21,7 +22,7 @@ public sealed record PassiveHealthCheckConfig
2122
public string? Policy { get; init; }
2223

2324
/// <summary>
24-
/// Destination reactivation period after which an unhealthy destination is considered healthy again.
25+
/// Destination reactivation period after which an unhealthy destination reverts back to <see cref="DestinationHealth.Unknown"/>.
2526
/// </summary>
2627
public TimeSpan? ReactivationPeriod { get; init; }
2728

src/ReverseProxy/Configuration/SessionAffinityConfig.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public sealed record SessionAffinityConfig
3030
/// Identifies the name of the field where the affinity value is stored.
3131
/// For the cookie affinity policy this will be the cookie name.
3232
/// For the header affinity policy this will be the header name.
33-
/// The policy will give its own default if no value is set.
3433
/// This value should be unique across clusters to avoid affinity conflicts.
3534
/// https://github.com/dotnet/yarp/issues/976
3635
/// This field is required.

0 commit comments

Comments
 (0)