mirror of https://github.com/dapr/dotnet-sdk.git
Wait for sidecar in DaprSecretStoreConfiguration (#838)
The secret store configuration provider was trying to access Dapr during the app startup. If the app started faster than Dapr, it would get an error trying to access the secrets. This commit lets the provider wait for the sidecar to come up before making any requests. If the sidecar does not come up at all, we will still fail. https://github.com/dapr/dotnet-sdk/issues/779 Signed-off-by: Hal Spang <halspang@microsoft.com>
This commit is contained in:
parent
dca6106af7
commit
a6319dd4b6
|
@ -19,6 +19,7 @@
|
|||
using Dapr.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Secret Store Configuration Provider Sample.
|
||||
|
@ -62,7 +63,8 @@
|
|||
// configBuilder.AddDaprSecretStore("demosecrets", secretDescriptors, client);
|
||||
|
||||
// Add the secret store Configuration Provider to the configuration builder.
|
||||
configBuilder.AddDaprSecretStore("demosecrets", client);
|
||||
// Including a TimeSpan allows us to dictate how long we should wait for the Sidecar to start.
|
||||
configBuilder.AddDaprSecretStore("demosecrets", client, TimeSpan.FromSeconds(10));
|
||||
})
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
|
|
|
@ -313,6 +313,23 @@ namespace Dapr.Client
|
|||
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
|
||||
/// <returns>A <see cref="Task{T}" /> that will return the value when the operation has completed.</returns>
|
||||
public abstract Task<bool> CheckHealthAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Perform health-check of Dapr sidecar's outbound APIs. Return 'true' if the sidecar is healthy. Otherwise false. This method should
|
||||
/// be used over <see cref="CheckHealthAsync(CancellationToken)"/> when the health of Dapr is being checked before it starts. This
|
||||
/// health endpoint indicates that Dapr has stood up its APIs and is currently waiting on this application to report fully healthy.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
|
||||
/// <returns>A <see cref="Task{T}" /> that will return the value when the operation has completed.</returns>
|
||||
public abstract Task<bool> CheckOutboundHealthAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="CheckOutboundHealthAsync(CancellationToken)"/> until the sidecar is reporting as healthy. If the sidecar
|
||||
/// does not become healthy, an exception will be thrown.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
|
||||
/// <returns>A <see cref="Task" /> that will return when the operation has completed.</returns>
|
||||
public abstract Task WaitForSidecarAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Perform service invocation using the request provided by <paramref name="request" />. The response will
|
||||
|
|
|
@ -305,6 +305,34 @@ namespace Dapr.Client
|
|||
}
|
||||
}
|
||||
|
||||
public override async Task<bool> CheckOutboundHealthAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var path = "/v1.0/healthz/outbound";
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, new Uri(this.httpEndpoint, path));
|
||||
try
|
||||
{
|
||||
var response = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task WaitForSidecarAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var response = await CheckOutboundHealthAsync(cancellationToken);
|
||||
if (response)
|
||||
{
|
||||
break;
|
||||
}
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<HttpResponseMessage> InvokeMethodWithResponseAsync(HttpRequestMessage request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNull(request, nameof(request));
|
||||
|
|
|
@ -17,6 +17,7 @@ using Dapr.Client;
|
|||
using Microsoft.Extensions.Configuration;
|
||||
using Dapr.Extensions.Configuration.DaprSecretStore;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Dapr.Extensions.Configuration
|
||||
{
|
||||
|
@ -53,6 +54,37 @@ namespace Dapr.Extensions.Configuration
|
|||
return configurationBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from the Dapr Secret Store.
|
||||
/// </summary>
|
||||
/// <param name="configurationBuilder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||
/// <param name="store">Dapr secret store name.</param>
|
||||
/// <param name="secretDescriptors">The secrets to retrieve.</param>
|
||||
/// <param name="client">The Dapr client.</param>
|
||||
/// <param name="sidecarWaitTimeout">The <see cref="TimeSpan"/> used to configure the timeout waiting for Dapr.</param>
|
||||
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||
public static IConfigurationBuilder AddDaprSecretStore(
|
||||
this IConfigurationBuilder configurationBuilder,
|
||||
string store,
|
||||
IEnumerable<DaprSecretDescriptor> secretDescriptors,
|
||||
DaprClient client,
|
||||
TimeSpan sidecarWaitTimeout)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store));
|
||||
ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors));
|
||||
ArgumentVerifier.ThrowIfNull(client, nameof(client));
|
||||
|
||||
configurationBuilder.Add(new DaprSecretStoreConfigurationSource()
|
||||
{
|
||||
Store = store,
|
||||
SecretDescriptors = secretDescriptors,
|
||||
Client = client,
|
||||
SidecarWaitTimeout = sidecarWaitTimeout
|
||||
});
|
||||
|
||||
return configurationBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from the Dapr Secret Store.
|
||||
/// </summary>
|
||||
|
@ -80,6 +112,36 @@ namespace Dapr.Extensions.Configuration
|
|||
return configurationBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from the Dapr Secret Store.
|
||||
/// </summary>
|
||||
/// <param name="configurationBuilder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||
/// <param name="store">Dapr secret store name.</param>
|
||||
/// <param name="metadata">A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used.</param>
|
||||
/// <param name="client">The Dapr client</param>
|
||||
/// <param name="sidecarWaitTimeout">The <see cref="TimeSpan"/> used to configure the timeout waiting for Dapr.</param>
|
||||
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||
public static IConfigurationBuilder AddDaprSecretStore(
|
||||
this IConfigurationBuilder configurationBuilder,
|
||||
string store,
|
||||
DaprClient client,
|
||||
TimeSpan sidecarWaitTimeout,
|
||||
IReadOnlyDictionary<string, string>? metadata = null)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store));
|
||||
ArgumentVerifier.ThrowIfNull(client, nameof(client));
|
||||
|
||||
configurationBuilder.Add(new DaprSecretStoreConfigurationSource()
|
||||
{
|
||||
Store = store,
|
||||
Metadata = metadata,
|
||||
Client = client,
|
||||
SidecarWaitTimeout = sidecarWaitTimeout
|
||||
});
|
||||
|
||||
return configurationBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from the Dapr Secret Store.
|
||||
/// </summary>
|
||||
|
@ -113,6 +175,42 @@ namespace Dapr.Extensions.Configuration
|
|||
return configurationBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from the Dapr Secret Store.
|
||||
/// </summary>
|
||||
/// <param name="configurationBuilder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||
/// <param name="store">Dapr secret store name.</param>
|
||||
/// <param name="keyDelimiters">A collection of delimiters that will be replaced by ':' in the key of every secret.</param>
|
||||
/// <param name="client">The Dapr client</param>
|
||||
/// <param name="sidecarWaitTimeout">The <see cref="TimeSpan"/> used to configure the timeout waiting for Dapr.</param>
|
||||
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||
public static IConfigurationBuilder AddDaprSecretStore(
|
||||
this IConfigurationBuilder configurationBuilder,
|
||||
string store,
|
||||
DaprClient client,
|
||||
IEnumerable<string>? keyDelimiters,
|
||||
TimeSpan sidecarWaitTimeout)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store));
|
||||
ArgumentVerifier.ThrowIfNull(client, nameof(client));
|
||||
|
||||
var source = new DaprSecretStoreConfigurationSource
|
||||
{
|
||||
Store = store,
|
||||
Client = client,
|
||||
SidecarWaitTimeout = sidecarWaitTimeout
|
||||
};
|
||||
|
||||
if (keyDelimiters != null)
|
||||
{
|
||||
source.KeyDelimiters = keyDelimiters.ToList();
|
||||
}
|
||||
|
||||
configurationBuilder.Add(source);
|
||||
|
||||
return configurationBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from the command line.
|
||||
/// </summary>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
@ -25,6 +26,8 @@ namespace Dapr.Extensions.Configuration.DaprSecretStore
|
|||
/// </summary>
|
||||
internal class DaprSecretStoreConfigurationProvider : ConfigurationProvider
|
||||
{
|
||||
internal static readonly TimeSpan DefaultSidecarWaitTimeout = TimeSpan.FromSeconds(5);
|
||||
|
||||
private readonly string store;
|
||||
|
||||
private readonly bool normalizeKey;
|
||||
|
@ -37,39 +40,56 @@ namespace Dapr.Extensions.Configuration.DaprSecretStore
|
|||
|
||||
private readonly DaprClient client;
|
||||
|
||||
private readonly TimeSpan sidecarWaitTimeout;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DaprSecretStoreConfigurationProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="store">Dapr Secre Store name.</param>
|
||||
/// <param name="store">Dapr Secret Store name.</param>
|
||||
/// <param name="normalizeKey">Indicates whether any key delimiters should be replaced with the delimiter ":".</param>
|
||||
/// <param name="secretDescriptors">The secrets to retrieve.</param>
|
||||
/// <param name="client">Dapr client used to retrieve Secrets</param>
|
||||
public DaprSecretStoreConfigurationProvider(string store, bool normalizeKey, IEnumerable<DaprSecretDescriptor> secretDescriptors, DaprClient client)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store));
|
||||
ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors));
|
||||
ArgumentVerifier.ThrowIfNull(client, nameof(client));
|
||||
|
||||
if (secretDescriptors.Count() == 0)
|
||||
{
|
||||
throw new ArgumentException("No secret descriptor was provided", nameof(secretDescriptors));
|
||||
}
|
||||
|
||||
this.store = store;
|
||||
this.normalizeKey = normalizeKey;
|
||||
this.secretDescriptors = secretDescriptors;
|
||||
this.client = client;
|
||||
public DaprSecretStoreConfigurationProvider(
|
||||
string store,
|
||||
bool normalizeKey,
|
||||
IEnumerable<DaprSecretDescriptor> secretDescriptors,
|
||||
DaprClient client) : this(store, normalizeKey, null, secretDescriptors, client, DefaultSidecarWaitTimeout)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DaprSecretStoreConfigurationProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="store">Dapr Secre Store name.</param>
|
||||
/// <param name="store">Dapr Secret Store name.</param>
|
||||
/// <param name="normalizeKey">Indicates whether any key delimiters should be replaced with the delimiter ":".</param>
|
||||
/// <param name="keyDelimiters">A collection of delimiters that will be replaced by ':' in the key of every secret.</param>
|
||||
/// <param name="secretDescriptors">The secrets to retrieve.</param>
|
||||
/// <param name="client">Dapr client used to retrieve Secrets</param>
|
||||
public DaprSecretStoreConfigurationProvider(string store, bool normalizeKey, IList<string>? keyDelimiters, IEnumerable<DaprSecretDescriptor> secretDescriptors, DaprClient client)
|
||||
public DaprSecretStoreConfigurationProvider(
|
||||
string store,
|
||||
bool normalizeKey,
|
||||
IList<string>? keyDelimiters,
|
||||
IEnumerable<DaprSecretDescriptor> secretDescriptors,
|
||||
DaprClient client) : this(store, normalizeKey, keyDelimiters, secretDescriptors, client, DefaultSidecarWaitTimeout)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DaprSecretStoreConfigurationProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="store">Dapr Secret Store name.</param>
|
||||
/// <param name="normalizeKey">Indicates whether any key delimiters should be replaced with the delimiter ":".</param>
|
||||
/// <param name="keyDelimiters">A collection of delimiters that will be replaced by ':' in the key of every secret.</param>
|
||||
/// <param name="secretDescriptors">The secrets to retrieve.</param>
|
||||
/// <param name="client">Dapr client used to retrieve Secrets</param>
|
||||
/// <param name="sidecarWaitTimeout">The <see cref="TimeSpan"/> used to configure the timeout waiting for Dapr.</param>
|
||||
public DaprSecretStoreConfigurationProvider(
|
||||
string store,
|
||||
bool normalizeKey,
|
||||
IList<string>? keyDelimiters,
|
||||
IEnumerable<DaprSecretDescriptor> secretDescriptors,
|
||||
DaprClient client,
|
||||
TimeSpan sidecarWaitTimeout)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store));
|
||||
ArgumentVerifier.ThrowIfNull(secretDescriptors, nameof(secretDescriptors));
|
||||
|
@ -85,35 +105,57 @@ namespace Dapr.Extensions.Configuration.DaprSecretStore
|
|||
this.keyDelimiters = keyDelimiters;
|
||||
this.secretDescriptors = secretDescriptors;
|
||||
this.client = client;
|
||||
this.sidecarWaitTimeout = sidecarWaitTimeout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DaprSecretStoreConfigurationProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="store">Dapr Secre Store name.</param>
|
||||
/// <param name="store">Dapr Secret Store name.</param>
|
||||
/// <param name="normalizeKey">Indicates whether any key delimiters should be replaced with the delimiter ":".</param>
|
||||
/// <param name="metadata">A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used.</param>
|
||||
/// <param name="client">Dapr client used to retrieve Secrets</param>
|
||||
public DaprSecretStoreConfigurationProvider(string store, bool normalizeKey, IReadOnlyDictionary<string, string>? metadata, DaprClient client)
|
||||
public DaprSecretStoreConfigurationProvider(
|
||||
string store,
|
||||
bool normalizeKey,
|
||||
IReadOnlyDictionary<string, string>? metadata,
|
||||
DaprClient client) : this(store, normalizeKey, null, metadata, client, DefaultSidecarWaitTimeout)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store));
|
||||
ArgumentVerifier.ThrowIfNull(client, nameof(client));
|
||||
|
||||
this.store = store;
|
||||
this.normalizeKey = normalizeKey;
|
||||
this.metadata = metadata;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DaprSecretStoreConfigurationProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="store">Dapr Secre Store name.</param>
|
||||
/// <param name="store">Dapr Secret Store name.</param>
|
||||
/// <param name="normalizeKey">Indicates whether any key delimiters should be replaced with the delimiter ":".</param>
|
||||
/// <param name="keyDelimiters">A collection of delimiters that will be replaced by ':' in the key of every secret.</param>
|
||||
/// <param name="metadata">A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used.</param>
|
||||
/// <param name="client">Dapr client used to retrieve Secrets</param>
|
||||
public DaprSecretStoreConfigurationProvider(string store, bool normalizeKey, IList<string>? keyDelimiters, IReadOnlyDictionary<string, string>? metadata, DaprClient client)
|
||||
public DaprSecretStoreConfigurationProvider(
|
||||
string store,
|
||||
bool normalizeKey,
|
||||
IList<string>? keyDelimiters,
|
||||
IReadOnlyDictionary<string, string>? metadata,
|
||||
DaprClient client) : this(store, normalizeKey, keyDelimiters, metadata, client, DefaultSidecarWaitTimeout)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DaprSecretStoreConfigurationProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="store">Dapr Secret Store name.</param>
|
||||
/// <param name="normalizeKey">Indicates whether any key delimiters should be replaced with the delimiter ":".</param>
|
||||
/// <param name="keyDelimiters">A collection of delimiters that will be replaced by ':' in the key of every secret.</param>
|
||||
/// <param name="metadata">A collection of metadata key-value pairs that will be provided to the secret store. The valid metadata keys and values are determined by the type of secret store used.</param>
|
||||
/// <param name="client">Dapr client used to retrieve Secrets</param>
|
||||
/// <param name="sidecarWaitTimeout">The <see cref="TimeSpan"/> used to configure the timeout waiting for Dapr.</param>
|
||||
public DaprSecretStoreConfigurationProvider(
|
||||
string store,
|
||||
bool normalizeKey,
|
||||
IList<string>? keyDelimiters,
|
||||
IReadOnlyDictionary<string, string>? metadata,
|
||||
DaprClient client,
|
||||
TimeSpan sidecarWaitTimeout)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(store, nameof(store));
|
||||
ArgumentVerifier.ThrowIfNull(client, nameof(client));
|
||||
|
@ -123,6 +165,7 @@ namespace Dapr.Extensions.Configuration.DaprSecretStore
|
|||
this.keyDelimiters = keyDelimiters;
|
||||
this.metadata = metadata;
|
||||
this.client = client;
|
||||
this.sidecarWaitTimeout = sidecarWaitTimeout;
|
||||
}
|
||||
|
||||
private string NormalizeKey(string key)
|
||||
|
@ -144,6 +187,12 @@ namespace Dapr.Extensions.Configuration.DaprSecretStore
|
|||
{
|
||||
var data = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
// Wait for the Dapr Sidecar to report healthy before attempting to fetch secrets.
|
||||
using (var tokenSource = new CancellationTokenSource(sidecarWaitTimeout))
|
||||
{
|
||||
await client.WaitForSidecarAsync(tokenSource.Token);
|
||||
}
|
||||
|
||||
if (secretDescriptors != null)
|
||||
{
|
||||
foreach (var secretDescriptor in secretDescriptors)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Dapr.Client;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
|
@ -54,6 +55,11 @@ namespace Dapr.Extensions.Configuration.DaprSecretStore
|
|||
/// </summary>
|
||||
public DaprClient Client { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TimeSpan"/> that is used to control the timeout waiting for the Dapr sidecar to become healthly.
|
||||
/// </summary>
|
||||
public TimeSpan? SidecarWaitTimeout { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConfigurationProvider Build(IConfigurationBuilder builder)
|
||||
{
|
||||
|
@ -64,11 +70,11 @@ namespace Dapr.Extensions.Configuration.DaprSecretStore
|
|||
throw new ArgumentException($"{nameof(Metadata)} must be null when {nameof(SecretDescriptors)} is set", nameof(Metadata));
|
||||
}
|
||||
|
||||
return new DaprSecretStoreConfigurationProvider(Store, NormalizeKey, KeyDelimiters, SecretDescriptors, Client);
|
||||
return new DaprSecretStoreConfigurationProvider(Store, NormalizeKey, KeyDelimiters, SecretDescriptors, Client, SidecarWaitTimeout ?? DaprSecretStoreConfigurationProvider.DefaultSidecarWaitTimeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new DaprSecretStoreConfigurationProvider(Store, NormalizeKey, KeyDelimiters, Metadata, Client);
|
||||
return new DaprSecretStoreConfigurationProvider(Store, NormalizeKey, KeyDelimiters, Metadata, Client, SidecarWaitTimeout ?? DaprSecretStoreConfigurationProvider.DefaultSidecarWaitTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace Dapr.Client.Test
|
|||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client;
|
||||
using Xunit;
|
||||
|
@ -308,6 +309,78 @@ namespace Dapr.Client.Test
|
|||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckOutboundHealthAsync_Success()
|
||||
{
|
||||
await using var client = TestClient.CreateForDaprClient(c =>
|
||||
{
|
||||
c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions);
|
||||
});
|
||||
var request = await client.CaptureHttpRequestAsync<bool>(async daprClient => await daprClient.CheckOutboundHealthAsync());
|
||||
|
||||
Assert.Equal(request.Request.Method, HttpMethod.Get);
|
||||
Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri);
|
||||
|
||||
var result = await request.CompleteAsync(new HttpResponseMessage());
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckOutboundHealthAsync_NotSuccess()
|
||||
{
|
||||
await using var client = TestClient.CreateForDaprClient(c =>
|
||||
{
|
||||
c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions);
|
||||
});
|
||||
var request = await client.CaptureHttpRequestAsync<bool>(async daprClient => await daprClient.CheckOutboundHealthAsync());
|
||||
|
||||
Assert.Equal(request.Request.Method, HttpMethod.Get);
|
||||
Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri);
|
||||
|
||||
var result = await request.CompleteAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError));
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckOutboundHealthAsync_WrapsRequestException()
|
||||
{
|
||||
await using var client = TestClient.CreateForDaprClient(c =>
|
||||
{
|
||||
c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions);
|
||||
});
|
||||
var request = await client.CaptureHttpRequestAsync<bool>(async daprClient => await daprClient.CheckOutboundHealthAsync());
|
||||
|
||||
Assert.Equal(request.Request.Method, HttpMethod.Get);
|
||||
Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/healthz/outbound").AbsoluteUri, request.Request.RequestUri.AbsoluteUri);
|
||||
|
||||
var result = await request.CompleteWithExceptionAndResultAsync(new HttpRequestException());
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WaitForSidecarAsync_SuccessWhenSidecarHealthy()
|
||||
{
|
||||
await using var client = TestClient.CreateForDaprClient();
|
||||
var request = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.WaitForSidecarAsync());
|
||||
|
||||
// If we don't throw, we're good.
|
||||
await request.CompleteAsync(new HttpResponseMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WaitForSidecarAsync_NotSuccessWhenSidecarNotHealthy()
|
||||
{
|
||||
await using var client = TestClient.CreateForDaprClient();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var waitRequest = await client.CaptureHttpRequestAsync(async daprClient => await daprClient.WaitForSidecarAsync(cts.Token));
|
||||
var healthRequest = await client.CaptureHttpRequestAsync<bool>(async daprClient => await daprClient.CheckOutboundHealthAsync());
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
await healthRequest.CompleteAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError));
|
||||
await Assert.ThrowsAsync<TaskCanceledException>(async () => await waitRequest.CompleteWithExceptionAsync(new TaskCanceledException()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeMethodAsync_WrapsHttpRequestException()
|
||||
{
|
||||
|
|
|
@ -125,6 +125,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -152,6 +153,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -178,6 +180,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -250,6 +253,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -277,6 +281,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -303,6 +308,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -328,6 +334,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -353,6 +360,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -377,6 +385,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -408,6 +417,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -440,6 +450,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -473,6 +484,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -506,6 +518,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -531,25 +544,33 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
Handler = async (entry) =>
|
||||
{
|
||||
// The following is an attempt at handling multiple secret descriptors for unit tests.
|
||||
var content = await entry.Request.Content.ReadAsStringAsync();
|
||||
if (content.Contains("secretName--value"))
|
||||
if (entry.Request.RequestUri.AbsoluteUri.Contains("healthz"))
|
||||
{
|
||||
await SendResponseWithSecrets(new Dictionary<string, string>()
|
||||
{
|
||||
["secretName--value"] = "secret",
|
||||
}, entry);
|
||||
await SendEmptyResponse(entry);
|
||||
}
|
||||
else if (content.Contains("otherSecretName≡value"))
|
||||
else
|
||||
{
|
||||
await SendResponseWithSecrets(new Dictionary<string, string>()
|
||||
var content = await entry.Request.Content.ReadAsStringAsync();
|
||||
if (content.Contains("secretName--value"))
|
||||
{
|
||||
["otherSecretName≡value"] = "secret",
|
||||
}, entry);
|
||||
}
|
||||
await SendResponseWithSecrets(new Dictionary<string, string>()
|
||||
{
|
||||
["secretName--value"] = "secret",
|
||||
}, entry);
|
||||
}
|
||||
else if (content.Contains("otherSecretName≡value"))
|
||||
{
|
||||
await SendResponseWithSecrets(new Dictionary<string, string>()
|
||||
{
|
||||
["otherSecretName≡value"] = "secret",
|
||||
}, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -588,6 +609,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -616,6 +638,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -646,6 +669,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -675,6 +699,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -709,6 +734,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
|
@ -725,6 +751,27 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
config["first_secret--value"].Should().Be("secret1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadSecrets_FailsIfSidecarNotAvailable()
|
||||
{
|
||||
var httpClient = new TestHttpClient()
|
||||
{
|
||||
Handler = async (entry) =>
|
||||
{
|
||||
await SendEmptyResponse(entry, HttpStatusCode.InternalServerError);
|
||||
}
|
||||
};
|
||||
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseHttpClientFactory(() => httpClient)
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
Assert.Throws<TaskCanceledException>(() => CreateBuilder()
|
||||
.AddDaprSecretStore("store", daprClient, TimeSpan.FromMilliseconds(1))
|
||||
.Build());
|
||||
}
|
||||
|
||||
private IConfigurationBuilder CreateBuilder()
|
||||
{
|
||||
return new ConfigurationBuilder();
|
||||
|
@ -755,5 +802,12 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
var response = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent);
|
||||
entry.Completion.SetResult(response);
|
||||
}
|
||||
|
||||
private async Task SendEmptyResponse(TestHttpClient.Entry entry, HttpStatusCode code = HttpStatusCode.OK)
|
||||
{
|
||||
var response = new Autogenerated.GetSecretResponse();
|
||||
var streamContent = await GrpcUtils.CreateResponseContent(response);
|
||||
entry.Completion.SetResult(GrpcUtils.CreateResponse(code, streamContent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue