Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,14 @@ OpenTelemetry.OpAmp.Client.Settings.OpAmpClientSettings.Identification.set -> vo
OpenTelemetry.OpAmp.Client.Settings.OpAmpClientSettings.InstanceUid.get -> System.Guid
OpenTelemetry.OpAmp.Client.Settings.OpAmpClientSettings.InstanceUid.set -> void
OpenTelemetry.OpAmp.Client.Settings.OpAmpClientSettings.OpAmpClientSettings() -> void
OpenTelemetry.OpAmp.Client.Settings.OpAmpClientSettings.RemoteConfiguration.get -> OpenTelemetry.OpAmp.Client.Settings.RemoteConfigSettings!
OpenTelemetry.OpAmp.Client.Settings.OpAmpClientSettings.RemoteConfiguration.set -> void
OpenTelemetry.OpAmp.Client.Settings.OpAmpClientSettings.ServerUrl.get -> System.Uri!
OpenTelemetry.OpAmp.Client.Settings.OpAmpClientSettings.ServerUrl.set -> void
OpenTelemetry.OpAmp.Client.Settings.RemoteConfigSettings
OpenTelemetry.OpAmp.Client.Settings.RemoteConfigSettings.AcceptsRemoteConfig.get -> bool
OpenTelemetry.OpAmp.Client.Settings.RemoteConfigSettings.AcceptsRemoteConfig.set -> void
OpenTelemetry.OpAmp.Client.Settings.RemoteConfigSettings.RemoteConfigSettings() -> void
override OpenTelemetry.OpAmp.Client.Settings.AnyValueUnion.Equals(object? obj) -> bool
override OpenTelemetry.OpAmp.Client.Settings.AnyValueUnion.GetHashCode() -> int
static OpenTelemetry.OpAmp.Client.Settings.AnyValueUnion.operator !=(OpenTelemetry.OpAmp.Client.Settings.AnyValueUnion left, OpenTelemetry.OpAmp.Client.Settings.AnyValueUnion right) -> bool
Expand Down
3 changes: 3 additions & 0 deletions src/OpenTelemetry.OpAmp.Client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
* Expose public `RemoteConfigMessage`.
([#3614](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3614))

* Add settings for remote configuration and update advertised capabilities.
([#3618](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3618))

## 0.1.0-alpha.3

Released 2025-Nov-13
Expand Down
17 changes: 14 additions & 3 deletions src/OpenTelemetry.OpAmp.Client/Internal/FrameBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,20 @@ IFrameBuilder IFrameBuilder.AddCapabilities()
this.EnsureInitialized();

// TODO: Update the actual capabilities when features are implemented.
this.currentMessage.Capabilities = (ulong)(AgentCapabilities.ReportsStatus
| AgentCapabilities.ReportsHealth
| AgentCapabilities.ReportsHeartbeat);

var capabilities = AgentCapabilities.ReportsStatus;

if (this.settings.Heartbeat.IsEnabled)
{
capabilities |= AgentCapabilities.ReportsHeartbeat | AgentCapabilities.ReportsHealth;
}

if (this.settings.RemoteConfiguration.AcceptsRemoteConfig)
{
capabilities |= AgentCapabilities.AcceptsRemoteConfig;
}

this.currentMessage.Capabilities = (ulong)capabilities;

return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ public Uri ServerUrl
/// </summary>
public HeartbeatSettings Heartbeat { get; set; } = new();

/// <summary>
/// Gets or sets the remote configuration settings.
/// </summary>
public RemoteConfigSettings RemoteConfiguration { get; set; } = new();

/// <summary>
/// Gets or sets the factory function called to create the <see
/// cref="HttpClient"/> instance that will be used at runtime to
Expand Down
19 changes: 19 additions & 0 deletions src/OpenTelemetry.OpAmp.Client/Settings/RemoteConfigSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

namespace OpenTelemetry.OpAmp.Client.Settings;

/// <summary>
/// Configuration settings for the remote configuration capability of the client.
/// </summary>
public sealed class RemoteConfigSettings
{
/// <summary>
/// Gets or sets a value indicating whether the client accepts remote configuration.
/// </summary>
/// <value>
/// <c>true</c> if remote configuration is accepted and should be sent by the server; otherwise, <c>false</c>.
/// Default is <c>false</c>.
/// </value>
public bool AcceptsRemoteConfig { get; set; }
}
78 changes: 78 additions & 0 deletions test/OpenTelemetry.OpAmp.Client.Tests/OpAmpClientTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using OpAmp.Proto.V1;
using OpenTelemetry.OpAmp.Client.Internal.Services.Heartbeat;
using OpenTelemetry.OpAmp.Client.Settings;
using OpenTelemetry.OpAmp.Client.Tests.Mocks;
using OpenTelemetry.OpAmp.Client.Tests.Tools;
using Xunit;
Expand Down Expand Up @@ -74,4 +76,80 @@ static ulong GetCurrentTimeInNanoseconds()
return (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1_000_000; // Convert to nanoseconds
}
}

[Fact]
public async Task DoesNotEmitHeartbeat_WhenDisabled()
{
using var opAmpServer = new OpAmpFakeHttpServer(false);
var opAmpEndpoint = opAmpServer.Endpoint;

using var mockListener = new MockListener();
using var client = new OpAmpClient(o =>
{
o.ServerUrl = opAmpEndpoint;
o.Heartbeat.IsEnabled = false;
});
client.Subscribe(mockListener);

await client.StartAsync();

mockListener.WaitForMessages(TimeSpan.FromSeconds(2));

var frames = opAmpServer.GetFrames();

// Only the identification message should be received
Assert.Single(frames);
Assert.Single(mockListener.Messages);

await client.StopAsync();
}

[Theory]
[ClassData(typeof(CapabilityTestData))]
internal async Task SendsExpectedCapabilities(
Action<OpAmpClientSettings> configure,
IEnumerable<AgentCapabilities> expectedCapabilities,
IEnumerable<AgentCapabilities> notExpectedCapabilities)
{
using var opAmpServer = new OpAmpFakeHttpServer(false);
var opAmpEndpoint = opAmpServer.Endpoint;
configure += o => o.ServerUrl = opAmpEndpoint;

using var mockListener = new MockListener();
using var client = new OpAmpClient(configure);
client.Subscribe(mockListener);

await client.StartAsync();

var frames = opAmpServer.GetFrames();

Assert.True(frames.Count >= 1, "Expecting at least one server frame.");

var identificationFrame = frames[0];
var capabilities = (AgentCapabilities)identificationFrame.Capabilities;

foreach (var expectedCapability in expectedCapabilities)
{
Assert.True(capabilities.HasFlag(expectedCapability), $"Expected capabilities to include {expectedCapability}.");
}

foreach (var notExpectedCapability in notExpectedCapabilities)
{
Assert.False(capabilities.HasFlag(notExpectedCapability), $"Expected capabilities not to include {notExpectedCapability}.");
}

await client.StopAsync();
}

internal class CapabilityTestData
: TheoryData<Action<OpAmpClientSettings>, IEnumerable<AgentCapabilities>, IEnumerable<AgentCapabilities>>
{
public CapabilityTestData()
{
this.Add(o => o.Heartbeat.IsEnabled = false, [], [AgentCapabilities.ReportsHeartbeat, AgentCapabilities.ReportsHealth]);
this.Add(o => o.Heartbeat.IsEnabled = true, [AgentCapabilities.ReportsHeartbeat, AgentCapabilities.ReportsHealth], []);
this.Add(o => o.RemoteConfiguration.AcceptsRemoteConfig = true, [AgentCapabilities.AcceptsRemoteConfig], []);
this.Add(o => o.RemoteConfiguration.AcceptsRemoteConfig = false, [], [AgentCapabilities.AcceptsRemoteConfig]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public async Task PlainHttpTransport_SendReceiveCommunication(bool useSmallPacke
var clientReceivedFrames = mockListener.Messages;
var receivedTextData = clientReceivedFrames.First().CustomMessage.Data.ToStringUtf8();

Assert.Single(serverReceivedFrames);
Assert.Equal(mockFrame.Uid, serverReceivedFrames.First().InstanceUid);
var frame = Assert.Single(serverReceivedFrames);
Assert.Equal(mockFrame.Uid, frame.InstanceUid);

Assert.Single(clientReceivedFrames);
Assert.StartsWith("This is a mock server frame for testing purposes.", receivedTextData);
Expand Down Expand Up @@ -71,7 +71,6 @@ public async Task PlainHttpTransport_UsesConfiguredHttpClientFactory()
frameProcessor.Subscribe(mockListener);

var httpTransport = new PlainHttpTransport(settings, frameProcessor);

var mockFrame = FrameGenerator.GenerateMockAgentFrame(false);

// Act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public OpAmpFakeHttpServer(bool useSmallPackets)

public Uri Endpoint { get; }

public IReadOnlyCollection<AgentToServer> GetFrames()
public IReadOnlyList<AgentToServer> GetFrames()
{
return this.frames.ToArray();
}
Expand Down