Skip to content

Commit 2dde24d

Browse files
Support Azure Front Door (#706)
* wip * wip * wip * wip * fix test * wip * wip * wip * add test * update correlation context * update * only check response time for refresh trigger engine * disallow sentinel key refresh for AFD * add feature flag refresh test * update * resolve comments * update * update error message * upgrade sdk version * update test
1 parent 6809fdc commit 2dde24d

25 files changed

+1020
-215
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
using Azure.Data.AppConfiguration;
5+
using Microsoft.Extensions.Azure;
6+
using System;
7+
using System.Collections.Generic;
8+
9+
namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Afd
10+
{
11+
internal class AfdConfigurationClientManager : IConfigurationClientManager
12+
{
13+
private readonly ConfigurationClientWrapper _clientWrapper;
14+
15+
public AfdConfigurationClientManager(
16+
IAzureClientFactory<ConfigurationClient> clientFactory,
17+
Uri endpoint)
18+
{
19+
if (clientFactory == null)
20+
{
21+
throw new ArgumentNullException(nameof(clientFactory));
22+
}
23+
24+
if (endpoint == null)
25+
{
26+
throw new ArgumentNullException(nameof(endpoint));
27+
}
28+
29+
_clientWrapper = new ConfigurationClientWrapper(endpoint, clientFactory.CreateClient(endpoint.AbsoluteUri));
30+
}
31+
32+
public IEnumerable<ConfigurationClient> GetClients()
33+
{
34+
return new List<ConfigurationClient> { _clientWrapper.Client };
35+
}
36+
37+
public void RefreshClients()
38+
{
39+
return;
40+
}
41+
42+
public bool UpdateSyncToken(Uri endpoint, string syncToken)
43+
{
44+
return false;
45+
}
46+
47+
public Uri GetEndpointForClient(ConfigurationClient client)
48+
{
49+
if (client == null)
50+
{
51+
throw new ArgumentNullException(nameof(client));
52+
}
53+
54+
return _clientWrapper.Client == client ? _clientWrapper.Endpoint : null;
55+
}
56+
}
57+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
using Azure.Core;
5+
using Azure.Core.Pipeline;
6+
using System;
7+
8+
namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Afd
9+
{
10+
/// <summary>
11+
/// HTTP pipeline policy that removes Authorization and Sync-Token headers from outgoing requests.
12+
/// </summary>
13+
internal class AfdPolicy : HttpPipelinePolicy
14+
{
15+
private const string AuthorizationHeader = "Authorization";
16+
private const string SyncTokenHeader = "Sync-Token";
17+
18+
/// <summary>
19+
/// Processes the HTTP message and removes Authorization and Sync-Token headers.
20+
/// </summary>
21+
/// <param name="message">The HTTP message.</param>
22+
/// <param name="pipeline">The pipeline.</param>
23+
public override void Process(HttpMessage message, ReadOnlyMemory<HttpPipelinePolicy> pipeline)
24+
{
25+
message.Request.Headers.Remove(AuthorizationHeader);
26+
27+
message.Request.Headers.Remove(SyncTokenHeader);
28+
29+
ProcessNext(message, pipeline);
30+
}
31+
32+
/// <summary>
33+
/// Processes the HTTP message and removes Authorization and Sync-Token headers.
34+
/// </summary>
35+
/// <param name="message">The HTTP message.</param>
36+
/// <param name="pipeline">The pipeline.</param>
37+
/// <returns>A task representing the asynchronous operation.</returns>
38+
public override async System.Threading.Tasks.ValueTask ProcessAsync(HttpMessage message, ReadOnlyMemory<HttpPipelinePolicy> pipeline)
39+
{
40+
message.Request.Headers.Remove(AuthorizationHeader);
41+
42+
message.Request.Headers.Remove(SyncTokenHeader);
43+
44+
await ProcessNextAsync(message, pipeline).ConfigureAwait(false);
45+
}
46+
}
47+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
using Azure.Core;
5+
using System;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Afd
10+
{
11+
/// <summary>
12+
/// A token credential that provides an empty token.
13+
/// </summary>
14+
internal class EmptyTokenCredential : TokenCredential
15+
{
16+
/// <summary>
17+
/// Gets an empty token.
18+
/// </summary>
19+
/// <param name="requestContext">The context of the token request.</param>
20+
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
21+
/// <returns>An empty access token.</returns>
22+
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
23+
{
24+
return new AccessToken(string.Empty, DateTimeOffset.MaxValue);
25+
}
26+
27+
/// <summary>
28+
/// Asynchronously gets an empty token.
29+
/// </summary>
30+
/// <param name="requestContext">The context of the token request.</param>
31+
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
32+
/// <returns>A task that represents the asynchronous operation. The task result contains an empty access token.</returns>
33+
public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
34+
{
35+
return new ValueTask<AccessToken>(new AccessToken(string.Empty, DateTimeOffset.MaxValue));
36+
}
37+
}
38+
}

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Azure.Core;
55
using Azure.Data.AppConfiguration;
66
using Microsoft.Extensions.Azure;
7+
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Afd;
78
using Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault;
89
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
910
using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;
@@ -132,7 +133,7 @@ internal IEnumerable<IKeyValueAdapter> Adapters
132133
/// <summary>
133134
/// Options used to configure the client used to communicate with Azure App Configuration.
134135
/// </summary>
135-
internal ConfigurationClientOptions ClientOptions { get; } = GetDefaultClientOptions();
136+
internal ConfigurationClientOptions ClientOptions { get; private set; } = GetDefaultClientOptions();
136137

137138
/// <summary>
138139
/// Flag to indicate whether Key Vault options have been configured.
@@ -154,6 +155,11 @@ internal IEnumerable<IKeyValueAdapter> Adapters
154155
/// </summary>
155156
internal StartupOptions Startup { get; set; } = new StartupOptions();
156157

158+
/// <summary>
159+
/// Gets a value indicating whether Azure Front Door is used.
160+
/// </summary>
161+
internal bool IsAfdUsed { get; private set; }
162+
157163
/// <summary>
158164
/// Client factory that is responsible for creating instances of ConfigurationClient.
159165
/// </summary>
@@ -186,11 +192,12 @@ public AzureAppConfigurationOptions()
186192
public AzureAppConfigurationOptions SetClientFactory(IAzureClientFactory<ConfigurationClient> factory)
187193
{
188194
ClientFactory = factory ?? throw new ArgumentNullException(nameof(factory));
195+
189196
return this;
190197
}
191198

192199
/// <summary>
193-
/// Specify what key-values to include in the configuration provider.
200+
/// Specifies what key-values to include in the configuration provider.
194201
/// <see cref="Select"/> can be called multiple times to include multiple sets of key-values.
195202
/// </summary>
196203
/// <param name="keyFilter">
@@ -262,7 +269,7 @@ public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter
262269
}
263270

264271
/// <summary>
265-
/// Specify a snapshot and include its contained key-values in the configuration provider.
272+
/// Specifies a snapshot and include its contained key-values in the configuration provider.
266273
/// <see cref="SelectSnapshot"/> can be called multiple times to include key-values from multiple snapshots.
267274
/// </summary>
268275
/// <param name="name">The name of the snapshot in Azure App Configuration.</param>
@@ -351,7 +358,7 @@ public AzureAppConfigurationOptions Connect(string connectionString)
351358
throw new ArgumentNullException(nameof(connectionString));
352359
}
353360

354-
return Connect(new List<string> { connectionString });
361+
return Connect(new string[] { connectionString });
355362
}
356363

357364
/// <summary>
@@ -362,6 +369,11 @@ public AzureAppConfigurationOptions Connect(string connectionString)
362369
/// </param>
363370
public AzureAppConfigurationOptions Connect(IEnumerable<string> connectionStrings)
364371
{
372+
if (IsAfdUsed)
373+
{
374+
throw new InvalidOperationException(ErrorMessages.ConnectionConflict);
375+
}
376+
365377
if (connectionStrings == null || !connectionStrings.Any())
366378
{
367379
throw new ArgumentNullException(nameof(connectionStrings));
@@ -395,7 +407,7 @@ public AzureAppConfigurationOptions Connect(Uri endpoint, TokenCredential creden
395407
throw new ArgumentNullException(nameof(credential));
396408
}
397409

398-
return Connect(new List<Uri>() { endpoint }, credential);
410+
return Connect(new Uri[] { endpoint }, credential);
399411
}
400412

401413
/// <summary>
@@ -405,6 +417,11 @@ public AzureAppConfigurationOptions Connect(Uri endpoint, TokenCredential creden
405417
/// <param name="credential">Token credential to use to connect.</param>
406418
public AzureAppConfigurationOptions Connect(IEnumerable<Uri> endpoints, TokenCredential credential)
407419
{
420+
if (IsAfdUsed)
421+
{
422+
throw new InvalidOperationException(ErrorMessages.ConnectionConflict);
423+
}
424+
408425
if (endpoints == null || !endpoints.Any())
409426
{
410427
throw new ArgumentNullException(nameof(endpoints));
@@ -416,12 +433,40 @@ public AzureAppConfigurationOptions Connect(IEnumerable<Uri> endpoints, TokenCre
416433
}
417434

418435
Credential = credential ?? throw new ArgumentNullException(nameof(credential));
419-
420436
Endpoints = endpoints;
421437
ConnectionStrings = null;
422438
return this;
423439
}
424440

441+
/// <summary>
442+
/// Connect the provider to Azure Front Door endpoint.
443+
/// </summary>
444+
/// <param name="endpoint">The endpoint of the Azure Front Door instance to connect to.</param>
445+
public AzureAppConfigurationOptions ConnectAzureFrontDoor(Uri endpoint)
446+
{
447+
if ((Credential != null && !(Credential is EmptyTokenCredential)) || (ConnectionStrings?.Any() ?? false))
448+
{
449+
throw new InvalidOperationException(ErrorMessages.ConnectionConflict);
450+
}
451+
452+
if (IsAfdUsed)
453+
{
454+
throw new InvalidOperationException(ErrorMessages.AfdConnectionConflict);
455+
}
456+
457+
if (endpoint == null)
458+
{
459+
throw new ArgumentNullException(nameof(endpoint));
460+
}
461+
462+
Credential ??= new EmptyTokenCredential();
463+
464+
Endpoints = new Uri[] { endpoint };
465+
ConnectionStrings = null;
466+
IsAfdUsed = true;
467+
return this;
468+
}
469+
425470
/// <summary>
426471
/// Trims the provided prefix from the keys of all key-values retrieved from Azure App Configuration.
427472
/// </summary>

0 commit comments

Comments
 (0)