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
1 change: 1 addition & 0 deletions .github/workflows/Steeltoe.All.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ jobs:
path: |
${{ github.workspace }}/TestOutput/**/*.dmp
${{ github.workspace }}/TestOutput/**/Sequence_*.xml
${{ github.workspace }}/TestOutput/**/*.binlog
if-no-files-found: ignore

- name: Report test results
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/component-shared-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ jobs:
path: |
${{ github.workspace }}/TestOutput/**/*.dmp
${{ github.workspace }}/TestOutput/**/Sequence_*.xml
${{ github.workspace }}/TestOutput/**/*.binlog
if-no-files-found: ignore

- name: Report test results
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/sonarcube.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,15 @@ jobs:
run: dotnet build ${{ env.SOLUTION_FILE }} --no-restore --configuration Release --verbosity minimal /p:NuGetAuditLevel=low /p:WarningsNotAsErrors='${{ env.NUGET_VULNERABLE_PACKAGE_WARNINGS }}'

- name: Test
id: test
run: dotnet test ${{ env.SOLUTION_FILE }} --filter "Category!=MemoryDumps&Category!=GitProperties" ${{ env.COMMON_TEST_ARGS }} ${{ env.COVERAGE_COLLECT_ARGS }}

- name: Test (memory dumps)
id: test-memory-dumps
run: dotnet test src/Management/test/Endpoint.Test --filter "Category=MemoryDumps" ${{ env.COMMON_TEST_ARGS }} ${{ env.COVERAGE_COLLECT_ARGS }}

- name: Test (git.properties)
id: test-gitproperties
run: >-
dotnet dotnet-coverage collect -f cobertura -o ${{ github.workspace }}/TestOutput/GitProperties.Build.Test.cobertura.xml --
dotnet test src/Management/test/GitProperties.Build.Test ${{ env.COMMON_TEST_ARGS }}
Expand All @@ -119,6 +122,17 @@ jobs:
-RepoRoot ${{ github.workspace }}
-ProjectRelativePath src/Management/src/GitProperties.Build

- name: Upload crash/hang dumps (on failure)
if: ${{ !cancelled() && (steps.test.outcome == 'failure' || steps.test-memory-dumps.outcome == 'failure' || steps.test-gitproperties.outcome == 'failure') }}
uses: actions/upload-artifact@v7
with:
name: FailedTestOutput
path: |
${{ github.workspace }}/TestOutput/**/*.dmp
${{ github.workspace }}/TestOutput/**/Sequence_*.xml
${{ github.workspace }}/TestOutput/**/*.binlog
if-no-files-found: ignore

- name: End Sonar .NET scanner
if: ${{ !cancelled() && steps.sonar_begin.outcome == 'success' }}
env:
Expand Down
75 changes: 0 additions & 75 deletions AGENTS.md

This file was deleted.

10 changes: 10 additions & 0 deletions src/Common/src/Common/EventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

namespace Steeltoe.Common;

internal sealed class EventArgs<T>(T value) : EventArgs
{
public T Value { get; } = value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,12 @@ public async Task CertificateOptions_update_on_changed_path(string certificateNa
string firstPrivateKeyFilePath = sandbox.CreateFile(Guid.NewGuid() + ".key", firstPrivateKeyContent);
using var secondX509 = X509Certificate2.CreateFromPemFile("secondInstance.crt", "secondInstance.key");
string appSettings = BuildAppSettingsJson(certificateName, firstCertificateFilePath, firstPrivateKeyFilePath);
string appSettingsPath = sandbox.CreateFile("appsettings.json", appSettings);

var fileProvider = new MemoryFileProvider();
fileProvider.IncludeAppSettingsJsonFile(appSettings);

var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile(appSettingsPath, false, true);
configurationBuilder.AddInMemoryAppSettingsJsonFile(fileProvider);
IConfiguration configuration = configurationBuilder.Build();

IServiceCollection services = new ServiceCollection();
Expand All @@ -204,10 +207,12 @@ public async Task CertificateOptions_update_on_changed_path(string certificateNa
var optionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<CertificateOptions>>();
optionsMonitor.Get(certificateName).Certificate.Should().BeEquivalentTo(firstX509);

await WaitUntilCertificateChangedToAsync(certificateName, secondX509, optionsMonitor, async () =>
await WaitUntilCertificateChangedToAsync(certificateName, secondX509, optionsMonitor, () =>
{
appSettings = BuildAppSettingsJson(certificateName, "secondInstance.crt", "secondInstance.key");
await File.WriteAllTextAsync(appSettingsPath, appSettings, TestContext.Current.CancellationToken);
fileProvider.ReplaceAppSettingsJsonFile(appSettings);
fileProvider.NotifyChanged();
return Task.CompletedTask;
});

optionsMonitor.Get(certificateName).Certificate.Should().Be(secondX509);
Expand Down
3 changes: 3 additions & 0 deletions src/Discovery/src/Eureka/EurekaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public sealed partial class EurekaClient
private readonly TimeProvider _timeProvider;
private readonly ILogger<EurekaClient> _logger;

internal event EventHandler<EventArgs<TimeSpan>>? HttpClientTimeoutConfigured;

public EurekaClient(IHttpClientFactory httpClientFactory, IOptionsMonitor<EurekaClientOptions> optionsMonitor,
EurekaServiceUriStateManager eurekaServiceUriStateManager, TimeProvider timeProvider, ILogger<EurekaClient> logger)
{
Expand Down Expand Up @@ -305,6 +307,7 @@ private HttpClient CreateHttpClient(string name, TimeSpan connectTimeout)
{
HttpClient httpClient = _httpClientFactory.CreateClient(name);
httpClient.ConfigureForSteeltoe(connectTimeout);
HttpClientTimeoutConfigured?.Invoke(this, new EventArgs<TimeSpan>(httpClient.Timeout));
return httpClient;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Runtime.InteropServices;
using FluentAssertions.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Expand Down Expand Up @@ -56,7 +55,7 @@ public async Task AddEurekaDiscoveryClient_UsesManagementOptions()
options.Value.StatusPageUrlPath.Should().Be("/actuator/info");
}

[FactSkippedOnPlatform(nameof(OSPlatform.OSX))]
[Fact]
public async Task AddEurekaDiscoveryClient_UsesServerTimeout()
{
var appSettings = new Dictionary<string, string?>
Expand All @@ -70,16 +69,15 @@ public async Task AddEurekaDiscoveryClient_UsesServerTimeout()
var services = new ServiceCollection();
services.AddSingleton(configuration);
services.AddEurekaDiscoveryClient();

await using ServiceProvider serviceProvider = services.BuildServiceProvider(true);

var timer = new Stopwatch();
timer.Start();
var eurekaClient = serviceProvider.GetRequiredService<EurekaClient>();
TimeSpan? capturedTimeout = null;
eurekaClient.HttpClientTimeoutConfigured += (_, args) => capturedTimeout = args.Value;

serviceProvider.GetServices<IDiscoveryClient>().Should().ContainSingle().Which.Should().BeOfType<EurekaDiscoveryClient>();

timer.Stop();
timer.ElapsedMilliseconds.Should().BeInRange(0, 3500);
capturedTimeout.Should().Be(1.Seconds());
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,8 @@ public sealed class ThreadDumpEndpointOptions : EndpointOptions
public override EndpointPermissions RequiredPermissions { get; set; } = EndpointPermissions.Full;

/// <summary>
/// Gets or sets the time (in milliseconds) to trace for, before automatically stopping the trace. Default value: 100.
/// Gets or sets how long to sample (in milliseconds). Higher values increase accuracy, at the cost of pausing the app for longer intervals. Default
/// value: 10.
/// </summary>
/// <remarks>
/// This is how long the EventPipe session captures samples from the "Microsoft-DotNETCore-SampleProfiler" provider, which samples on-CPU threads at a
/// fixed ~1 ms interval. Threads that are not scheduled on a CPU core during this window (for example, because they are idle or blocked waiting on I/O)
/// may get few or no samples, so a short duration risks missing threads entirely. A longer duration increases the odds of observing such threads, at the
/// cost of the endpoint taking longer to respond.
/// </remarks>
public int Duration { get; set; } = 100;
public int Duration { get; set; } = 10;
}
2 changes: 1 addition & 1 deletion src/Management/src/Endpoint/ConfigurationSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@
},
"Duration": {
"type": "integer",
"description": "Gets or sets the time (in milliseconds) to trace for, before automatically stopping the trace. Default value: 100."
"description": "Gets or sets how long to sample (in milliseconds). Higher values increase accuracy, at the cost of pausing the app for longer intervals. Default value: 10."
},
"Enabled": {
"type": "boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,15 @@ private async Task TimerLoopAsync(TimeSpan interval)

do
{
// A tick queued just before periodic refresh was disabled would still be delivered here.
// Checking the period prevents executing a stale tick when refresh has been turned off.
if (isFirstTime || _periodicTimer.Period != Timeout.InfiniteTimeSpan)
LogStartingRefreshCycle();

try
{
await _runner.RunAsync(isFirstTime, _timerTokenSource.Token);
}
catch (Exception exception) when (!exception.IsCancellation())
{
LogStartingRefreshCycle();

try
{
await _runner.RunAsync(isFirstTime, _timerTokenSource.Token);
}
catch (Exception exception) when (!exception.IsCancellation())
{
LogRefreshCycleFailed(exception);
}
LogRefreshCycleFailed(exception);
}

isFirstTime = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ public async Task RunAsync(bool isFirstTime, CancellationToken cancellationToken
{
LogValidatingOptions();
SpringBootAdminClientOptions clientOptions = _clientOptionsMonitor.CurrentValue;

if (!isFirstTime && clientOptions.RefreshInterval <= TimeSpan.Zero)
{
// Periodic refresh was turned off since this cycle got scheduled. Skip it, using the very same options snapshot that
// made this decision, so a stale tick can never end up registering with a server that a concurrent configuration
// change just switched away from (which reading a separately-updated flag, like a timer's Period, could allow).
return;
}

ValidateAndSetOptions(clientOptions);

if (_lastGoodOptions?.Url != null && !string.Equals(_lastGoodOptions.Url, clientOptions.Url, StringComparison.OrdinalIgnoreCase))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ namespace Steeltoe.Management.Endpoint.Test.Actuators.HeapDump;
[Trait("Category", "MemoryDumps")]
public sealed class HeapDumperTest
{
private static readonly TimeSpan DumpTimeout = TimeSpan.FromMinutes(3);
private static readonly TimeSpan DefaultDumpTimeout = TimeSpan.FromMinutes(3);
private static readonly TimeSpan LongDumpTimeout = TimeSpan.FromMinutes(5);

[Theory]
[InlineData(HeapDumpType.Full, "fulldump_", "full dump")]
Expand Down Expand Up @@ -45,7 +46,8 @@ public async Task Can_create_heap_dump(HeapDumpType heapDumpType, string fileNam
return dumper.DumpHeapToFile(TestContext.Current.CancellationToken);
}, TestContext.Current.CancellationToken);

string path = await dumpTask.WaitAsync(DumpTimeout, TestContext.Current.CancellationToken);
TimeSpan timeout = heapDumpType == HeapDumpType.Full ? LongDumpTimeout : DefaultDumpTimeout;
string path = await dumpTask.WaitAsync(timeout, TestContext.Current.CancellationToken);

path.Should().Contain(fileName);
File.Delete(path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,11 @@ public void Subscribe_to_listener_records_http_exchange()
HttpContext = context
});

SpinWait.SpinUntil(() => false, 1.Seconds());

activity.SetEndTime(activity.StartTimeUtc + 1.Seconds());
listener.StopActivity(activity, context);

HttpExchange httpExchange = recorded.Should().ContainSingle().Subject;
httpExchange.TimeTaken.Should().BeGreaterThan(900.Milliseconds()).And.BeLessThan(1300.Milliseconds());
httpExchange.TimeTaken.Should().Be(1.Seconds());
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ public async Task Can_resolve_source_location_from_pdb()
using var loggerFactory = new LoggerFactory([loggerProvider]);
ILogger<EventPipeThreadDumper> logger = loggerFactory.CreateLogger<EventPipeThreadDumper>();

var optionsMonitor = new TestOptionsMonitor<ThreadDumpEndpointOptions>();
var optionsMonitor = TestOptionsMonitor.Create(new ThreadDumpEndpointOptions
{
// For testing, sampling accuracy is more important than app pauses.
Duration = 100
});

var dumper = new EventPipeThreadDumper(optionsMonitor, logger);

StackTraceElement? callbackFrame = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public async Task Configures_default_settings()

ThreadDumpEndpointOptions options = serviceProvider.GetRequiredService<IOptions<ThreadDumpEndpointOptions>>().Value;

options.Duration.Should().Be(100);
options.Duration.Should().Be(10);
options.Enabled.Should().BeNull();
options.Id.Should().Be("threaddump");
options.Path.Should().Be("threaddump");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static async Task<PackageReference> PackAsync(DirectoryInfo sessionDirect
string projectFilePath = Path.Combine(sourceDirectory, $"{PackageId}.csproj");
await File.WriteAllTextAsync(projectFilePath, projectContent, TestContext.Current.CancellationToken);

await ProcessRunner.RunDotNetAsync(sourceDirectory, 0, null, "build", "-c", "Release", $"-p:Version={PackageVersion}",
await ProcessRunner.RunDotNetBuildCapturingDiagnosticsOnFailureAsync(sourceDirectory, "fake-endpoint-build", $"-p:Version={PackageVersion}",
$"-p:PackageOutputPath={nuGetFeedDirectory}");

return new PackageReference(PackageId, PackageVersion, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static async Task<PackageReference> PackAsync(string nuGetFeedDirectory)
string sourceDirectory = Path.Combine(repositoryRoot, "src", "Management", "src", "GitProperties.Build");
string packageVersion = $"{PackageVersionPrefix}.{$"{Guid.NewGuid():N}"[..8]}";

await ProcessRunner.RunDotNetAsync(sourceDirectory, 0, null, "build", "-c", "Release", $"-p:Version={packageVersion}",
await ProcessRunner.RunDotNetBuildCapturingDiagnosticsOnFailureAsync(sourceDirectory, "git-properties-build", $"-p:Version={packageVersion}",
$"-p:PackageOutputPath={nuGetFeedDirectory}");

return new PackageReference(PackageId, packageVersion, null);
Expand Down
Loading
Loading