Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix creating temporary dashboard client on startup
  • Loading branch information
JamesNK committed Oct 21, 2024
commit d9f62b5bb0bf422ca05f86fe45f45b9f18cba692
3 changes: 2 additions & 1 deletion src/Aspire.Dashboard/DashboardWebApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ public DashboardWebApplication(

// Data from the server.
builder.Services.TryAddScoped<IDashboardClient, DashboardClient>();
builder.Services.TryAddSingleton<IDashboardClientStatus, DashboardClientStatus>();

// OTLP services.
builder.Services.AddGrpc();
Expand Down Expand Up @@ -340,7 +341,7 @@ public DashboardWebApplication(
{
if (context.Request.Path.Equals(TargetLocationInterceptor.ResourcesPath, StringComparisons.UrlPath))
{
var client = context.RequestServices.GetRequiredService<IDashboardClient>();
var client = context.RequestServices.GetRequiredService<IDashboardClientStatus>();
if (!client.IsEnabled)
{
context.Response.Redirect(TargetLocationInterceptor.StructuredLogsPath);
Expand Down
10 changes: 6 additions & 4 deletions src/Aspire.Dashboard/ResourceService/DashboardClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace Aspire.Dashboard.Model;
/// If the <c>DOTNET_RESOURCE_SERVICE_ENDPOINT_URL</c> environment variable is not specified, then there's
/// no known endpoint to connect to, and this dashboard client will be disabled. Calls to
/// <see cref="IDashboardClient.SubscribeResourcesAsync"/> and <see cref="IDashboardClient.SubscribeConsoleLogs"/>
/// will throw if <see cref="IDashboardClient.IsEnabled"/> is <see langword="false"/>. Callers should
/// will throw if <see cref="IDashboardClientStatus.IsEnabled"/> is <see langword="false"/>. Callers should
/// check this property first, before calling these methods.
/// </para>
/// </remarks>
Expand All @@ -47,6 +47,7 @@ internal sealed class DashboardClient : IDashboardClient
private readonly object _lock = new();

private readonly ILoggerFactory _loggerFactory;
private readonly IDashboardClientStatus _dashboardClientStatus;
private readonly BrowserTimeProvider _timeProvider;
private readonly IKnownPropertyLookup _knownPropertyLookup;
private readonly DashboardOptions _dashboardOptions;
Expand All @@ -71,11 +72,13 @@ public DashboardClient(
ILoggerFactory loggerFactory,
IConfiguration configuration,
IOptions<DashboardOptions> dashboardOptions,
IDashboardClientStatus dashboardClientStatus,
BrowserTimeProvider timeProvider,
IKnownPropertyLookup knownPropertyLookup,
Action<SocketsHttpHandler>? configureHttpHandler = null)
{
_loggerFactory = loggerFactory;
_dashboardClientStatus = dashboardClientStatus;
_timeProvider = timeProvider;
_knownPropertyLookup = knownPropertyLookup;
_dashboardOptions = dashboardOptions.Value;
Expand All @@ -85,9 +88,7 @@ public DashboardClient(

_logger = loggerFactory.CreateLogger<DashboardClient>();

var address = _dashboardOptions.ResourceServiceClient.GetUri();

if (address is null)
if (!_dashboardClientStatus.IsEnabled)
{
_state = StateDisabled;
_logger.LogDebug($"{DashboardConfigNames.ResourceServiceUrlName.ConfigKey} is not specified. Dashboard client services are unavailable.");
Expand All @@ -96,6 +97,7 @@ public DashboardClient(
return;
}

var address = _dashboardOptions.ResourceServiceClient.GetUri()!;
_logger.LogDebug("Dashboard configured to connect to: {Address}", address);

// Create the gRPC channel. This channel performs automatic reconnects.
Expand Down
12 changes: 12 additions & 0 deletions src/Aspire.Dashboard/ResourceService/DashboardClientStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Dashboard.Configuration;
using Microsoft.Extensions.Options;

namespace Aspire.Dashboard.Model;

internal sealed class DashboardClientStatus(IOptions<DashboardOptions> dashboardOptions) : IDashboardClientStatus
{
public bool IsEnabled => dashboardOptions.Value.ResourceServiceClient.GetUri() is not null;
}
11 changes: 1 addition & 10 deletions src/Aspire.Dashboard/ResourceService/IDashboardClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,8 @@ namespace Aspire.Dashboard.Model;
/// <summary>
/// Provides data about active resources to external components, such as the dashboard.
/// </summary>
public interface IDashboardClient : IAsyncDisposable
public interface IDashboardClient : IDashboardClientStatus, IAsyncDisposable
{
/// <summary>
/// Gets whether this client object is enabled for use.
/// </summary>
/// <remarks>
/// Users of this client should check <see cref="IsEnabled"/> before calling
/// any other members of this interface, to avoid exceptions.
/// </remarks>
bool IsEnabled { get; }

Task WhenConnected { get; }

/// <summary>
Expand Down
16 changes: 16 additions & 0 deletions src/Aspire.Dashboard/ResourceService/IDashboardClientStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Aspire.Dashboard.Model;

public interface IDashboardClientStatus
{
/// <summary>
/// Gets whether the client object is enabled for use.
/// </summary>
/// <remarks>
/// Users of <see cref="IDashboardClient"/> client should check <see cref="IsEnabled"/> before calling
/// any other members of this interface, to avoid exceptions.
/// </remarks>
bool IsEnabled { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ private static async Task<DashboardClient> CreateDashboardClientAsync(
loggerFactory: loggerFactory,
configuration: new ConfigurationManager(),
dashboardOptions: Options.Create(options),
dashboardClientStatus: new TestDashboardClientStatus(),
timeProvider: new BrowserTimeProvider(NullLoggerFactory.Instance),
knownPropertyLookup: new MockKnownPropertyLookup(),
configureHttpHandler: handler => handler.SslOptions.RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true);
Expand Down Expand Up @@ -159,6 +160,11 @@ private sealed class TestCalls
public Channel<ReceivedCallInfo<ApplicationInformationRequest>> ApplicationInformationCallsChannel { get; } = Channel.CreateUnbounded<ReceivedCallInfo<ApplicationInformationRequest>>();
}

private sealed class TestDashboardClientStatus : IDashboardClientStatus
{
public bool IsEnabled => true;
}

private sealed class MockDashboardService(TestCalls testCalls) : DashboardServiceBase
{
public override Task<ApplicationInformationResponse> GetApplicationInformation(
Expand Down
7 changes: 6 additions & 1 deletion tests/Aspire.Dashboard.Tests/Model/DashboardClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ public async Task SubscribeResources_HasInitialData_InitialDataReturned()

private DashboardClient CreateResourceServiceClient()
{
return new DashboardClient(NullLoggerFactory.Instance, _configuration, _dashboardOptions, s_timeProvider, new MockKnownPropertyLookup());
return new DashboardClient(NullLoggerFactory.Instance, _configuration, _dashboardOptions, new TestDashboardClientStatus(), s_timeProvider, new MockKnownPropertyLookup());
}

private sealed class TestDashboardClientStatus : IDashboardClientStatus
{
public bool IsEnabled => true;
}
}