Skip to content

Commit de92136

Browse files
author
Rakeshwar Reddy Kambaiahgari
committed
commit changes and Profile Updates.
1 parent 7d5d972 commit de92136

22 files changed

Lines changed: 1199 additions & 423 deletions

src/VirtualClient/VirtualClient.Actions.UnitTests/AspNetBench/AspNetOrchardServerExecutorTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ public string DotNetExePath
204204
{
205205
base.Validate();
206206
}
207+
208+
protected override Task WaitForPortReadyAsync(EventContext context, CancellationToken cancellationToken)
209+
{
210+
return Task.CompletedTask;
211+
}
207212
}
208213

209214
private void SetupDefaultMockBehaviors(PlatformID platform)

src/VirtualClient/VirtualClient.Actions.UnitTests/AspNetBench/AspNetServerExecutorTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,11 @@ public string DotNetExePath
235235
{
236236
base.Validate();
237237
}
238+
239+
protected override Task WaitForPortReadyAsync(EventContext context, CancellationToken cancellationToken)
240+
{
241+
return Task.CompletedTask;
242+
}
238243
}
239244

240245
private void SetupDefaultMockBehaviors(PlatformID platform)

src/VirtualClient/VirtualClient.Actions.UnitTests/AspNetBench/BombardierExecutorTests.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,15 @@ public void BombardierExecutorGetBombardierVersionParsesVersionWithoutVPrefix()
7272
}
7373

7474
[Test]
75-
public void BombardierExecutorGetBombardierVersionThrowsOnUnparsableOutput()
75+
public void BombardierExecutorGetBombardierVersionReturnsNullOnUnparsableOutput()
7676
{
7777
this.mockFixture.SetupProcessOutput(".*--version.*", "unrecognized output");
7878

7979
using (TestBombardierExecutor executor = new TestBombardierExecutor(this.mockFixture.Dependencies, this.mockFixture.Parameters))
8080
{
8181
executor.PackageDirectory = this.mockPackage.Path;
82-
WorkloadException exception = Assert.Throws<WorkloadException>(
83-
() => executor.GetBombardierVersion(EventContext.None, CancellationToken.None));
84-
85-
Assert.AreEqual(ErrorReason.CriticalWorkloadFailure, exception.Reason);
82+
string version = executor.GetBombardierVersion(EventContext.None, CancellationToken.None);
83+
Assert.IsNull(version);
8684
}
8785
}
8886

src/VirtualClient/VirtualClient.Actions.UnitTests/Wrk/WrkExecutorTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ public void GetWrkVersionReturnsCorrectVersion()
593593
}
594594

595595
[Test]
596-
public void GetWrkVersion_ThrowsException_WhenVersionCannotBeParsed()
596+
public void GetWrkVersion_ReturnsNull_WhenVersionCannotBeParsed()
597597
{
598598
this.mockFixture.Setup(PlatformID.Unix, Architecture.X64);
599599
TestWrkExecutor executor = new TestWrkExecutor(this.mockFixture);
@@ -606,8 +606,8 @@ public void GetWrkVersion_ThrowsException_WhenVersionCannotBeParsed()
606606
.TrackProcesses()
607607
.SetupProcessOutput("--version", "Invalid output without version");
608608

609-
WorkloadException exception = Assert.Throws<WorkloadException>(() => executor.GetWrkVersion());
610-
Assert.AreEqual("Failed to parse wrk version from output.", exception.Message);
609+
string version = executor.GetWrkVersion();
610+
Assert.IsNull(version);
611611

612612
this.mockFixture.Tracking.AssertCommandsExecuted(true, "sudo bash .* --version");
613613
}

src/VirtualClient/VirtualClient.Actions/ASPNET/AspNetOrchardServerExecutor.cs

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace VirtualClient.Actions
99
using System.Linq;
1010
using System.Net;
1111
using System.Net.Http;
12+
using System.Net.Sockets;
1213
using System.Threading;
1314
using System.Threading.Tasks;
1415
using Microsoft.Extensions.DependencyInjection;
@@ -25,6 +26,7 @@ namespace VirtualClient.Actions
2526
/// <summary>
2627
/// AspNet Orchard Server Executor.
2728
/// </summary>
29+
[SupportedPlatforms("linux-arm64,linux-x64,win-arm64,win-x64")]
2830
public class AspNetOrchardServerExecutor : VirtualClientMultiRoleComponent
2931
{
3032
private Task serverProcess;
@@ -134,7 +136,7 @@ public string CoreAffinity
134136

135137
/// <summary>
136138
/// Disposes of resources used by the executor including shutting down any
137-
/// instances of Redis server running.
139+
/// instances of server running.
138140
/// </summary>
139141
protected override void Dispose(bool disposing)
140142
{
@@ -200,9 +202,16 @@ protected override void Validate()
200202
protected void InitializeApiClients()
201203
{
202204
IApiClientManager clientManager = this.Dependencies.GetService<IApiClientManager>();
203-
ClientInstance serverInstance = this.GetLayoutClientInstances(ClientRole.Server).First();
204205

205-
this.ServerApi = clientManager.GetOrCreateApiClient(serverInstance.Name, serverInstance);
206+
if (!this.IsMultiRoleLayout())
207+
{
208+
this.ServerApi = clientManager.GetOrCreateApiClient(IPAddress.Loopback.ToString(), IPAddress.Loopback);
209+
}
210+
else
211+
{
212+
ClientInstance serverInstance = this.GetLayoutClientInstances(ClientRole.Server).First();
213+
this.ServerApi = clientManager.GetOrCreateApiClient(serverInstance.Name, serverInstance);
214+
}
206215
}
207216

208217
/// <summary>
@@ -248,16 +257,28 @@ protected override Task ExecuteAsync(EventContext telemetryContext, Cancellation
248257
await this.KillServerInstancesAsync(telemetryContext, cancellationToken);
249258
await this.BuildAspNetOrchardAsync(telemetryContext, cancellationToken);
250259
this.StartServerInstances(telemetryContext, cancellationToken);
260+
await this.WaitForPortReadyAsync(telemetryContext, cancellationToken);
251261

252262
await this.SaveStateAsync(telemetryContext, cancellationToken);
253263
this.SetServerOnline(true);
254264

255-
using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
265+
if (!this.IsMultiRoleLayout())
266+
{
267+
// In single-VM mode, clear cleanup tasks to prevent the base class
268+
// CleanupAsync from killing the server process. The server must stay
269+
// alive for the subsequent client action. It will be killed by
270+
// KillServerInstancesAsync on the next iteration or by Dispose.
271+
this.CleanupTasks.Clear();
272+
}
273+
else
256274
{
257-
await Task.WhenAny(this.serverProcess);
258-
if (cancellationToken.IsCancellationRequested)
275+
using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
259276
{
260-
await Task.WhenAll(this.serverProcess);
277+
await Task.WhenAny(this.serverProcess);
278+
if (cancellationToken.IsCancellationRequested)
279+
{
280+
await Task.WhenAll(this.serverProcess);
281+
}
261282
}
262283
}
263284
}
@@ -270,6 +291,42 @@ protected override Task ExecuteAsync(EventContext telemetryContext, Cancellation
270291
});
271292
}
272293

294+
/// <summary>
295+
/// Waits for the configured port to start accepting TCP connections.
296+
/// </summary>
297+
/// <param name="telemetryContext">Provides context information that will be captured with telemetry events.</param>
298+
/// <param name="cancellationToken">A token that can be used to cancel the operation.</param>
299+
protected virtual async Task WaitForPortReadyAsync(EventContext telemetryContext, CancellationToken cancellationToken)
300+
{
301+
int port = int.Parse(this.ServerPort);
302+
TimeSpan timeout = TimeSpan.FromMinutes(5);
303+
DateTime deadline = DateTime.UtcNow.Add(timeout);
304+
305+
this.Logger.LogTraceMessage($"{this.TypeName}: Waiting for server to accept connections on port {port}...");
306+
307+
while (DateTime.UtcNow < deadline && !cancellationToken.IsCancellationRequested)
308+
{
309+
try
310+
{
311+
using (TcpClient client = new TcpClient())
312+
{
313+
await client.ConnectAsync(IPAddress.Loopback, port).ConfigureAwait(false);
314+
this.Logger.LogTraceMessage($"{this.TypeName}: Server is accepting connections on port {port}.");
315+
return;
316+
}
317+
}
318+
catch (SocketException)
319+
{
320+
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
321+
}
322+
}
323+
324+
cancellationToken.ThrowIfCancellationRequested();
325+
throw new WorkloadException(
326+
$"The server did not start accepting connections on port {port} within {timeout}.",
327+
ErrorReason.WorkloadFailed);
328+
}
329+
273330
private Task DeleteStateAsync(EventContext telemetryContext, CancellationToken cancellationToken)
274331
{
275332
EventContext relatedContext = telemetryContext.Clone();

src/VirtualClient/VirtualClient.Actions/ASPNET/AspNetServerExecutor.cs

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace VirtualClient.Actions
99
using System.Linq;
1010
using System.Net;
1111
using System.Net.Http;
12+
using System.Net.Sockets;
1213
using System.Threading;
1314
using System.Threading.Tasks;
1415
using Microsoft.Extensions.DependencyInjection;
@@ -214,10 +215,16 @@ protected override void Validate()
214215
protected void InitializeApiClients()
215216
{
216217
IApiClientManager clientManager = this.Dependencies.GetService<IApiClientManager>();
217-
bool isSingleVM = !this.IsMultiRoleLayout();
218218

219-
ClientInstance serverInstance = this.GetLayoutClientInstances(ClientRole.Server).First();
220-
this.ServerApi = clientManager.GetOrCreateApiClient(serverInstance.Name, serverInstance);
219+
if (!this.IsMultiRoleLayout())
220+
{
221+
this.ServerApi = clientManager.GetOrCreateApiClient(IPAddress.Loopback.ToString(), IPAddress.Loopback);
222+
}
223+
else
224+
{
225+
ClientInstance serverInstance = this.GetLayoutClientInstances(ClientRole.Server).First();
226+
this.ServerApi = clientManager.GetOrCreateApiClient(serverInstance.Name, serverInstance);
227+
}
221228
}
222229

223230
/// <summary>
@@ -240,17 +247,29 @@ protected override Task ExecuteAsync(EventContext telemetryContext, Cancellation
240247
await this.KillServerInstancesAsync(telemetryContext, cancellationToken);
241248
await this.BuildAspNetBenchAsync(telemetryContext, cancellationToken);
242249
this.StartServerInstances(telemetryContext, cancellationToken);
250+
await this.WaitForPortReadyAsync(telemetryContext, cancellationToken);
243251

244252
await this.SaveStateAsync(telemetryContext, cancellationToken);
245253
this.SetServerOnline(true);
246254

247-
using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
255+
if (!this.IsMultiRoleLayout())
248256
{
249-
await Task.WhenAny(this.serverProcess);
250-
251-
if (cancellationToken.IsCancellationRequested)
257+
// In single-VM mode, clear cleanup tasks to prevent the base class
258+
// CleanupAsync from killing the server process. The server must stay
259+
// alive for the subsequent client action. It will be killed by
260+
// KillServerInstancesAsync on the next iteration or by Dispose.
261+
this.CleanupTasks.Clear();
262+
}
263+
else
264+
{
265+
using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
252266
{
253-
await Task.WhenAll(this.serverProcess);
267+
await Task.WhenAny(this.serverProcess);
268+
269+
if (cancellationToken.IsCancellationRequested)
270+
{
271+
await Task.WhenAll(this.serverProcess);
272+
}
254273
}
255274
}
256275
}
@@ -283,6 +302,42 @@ await this.ExecuteCommandAsync(this.dotnetExePath, buildArgument, this.aspnetBen
283302
"Benchmarks.dll");
284303
}
285304

305+
/// <summary>
306+
/// Waits for the configured port to start accepting TCP connections.
307+
/// </summary>
308+
/// <param name="telemetryContext">Provides context information that will be captured with telemetry events.</param>
309+
/// <param name="cancellationToken">A token that can be used to cancel the operation.</param>
310+
protected virtual async Task WaitForPortReadyAsync(EventContext telemetryContext, CancellationToken cancellationToken)
311+
{
312+
int port = int.Parse(this.ServerPort);
313+
TimeSpan timeout = TimeSpan.FromMinutes(5);
314+
DateTime deadline = DateTime.UtcNow.Add(timeout);
315+
316+
this.Logger.LogTraceMessage($"{this.TypeName}: Waiting for server to accept connections on port {port}...");
317+
318+
while (DateTime.UtcNow < deadline && !cancellationToken.IsCancellationRequested)
319+
{
320+
try
321+
{
322+
using (TcpClient client = new TcpClient())
323+
{
324+
await client.ConnectAsync(IPAddress.Loopback, port).ConfigureAwait(false);
325+
this.Logger.LogTraceMessage($"{this.TypeName}: Server is accepting connections on port {port}.");
326+
return;
327+
}
328+
}
329+
catch (SocketException)
330+
{
331+
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
332+
}
333+
}
334+
335+
cancellationToken.ThrowIfCancellationRequested();
336+
throw new WorkloadException(
337+
$"The server did not start accepting connections on port {port} within {timeout}.",
338+
ErrorReason.WorkloadFailed);
339+
}
340+
286341
private Task DeleteStateAsync(EventContext telemetryContext, CancellationToken cancellationToken)
287342
{
288343
EventContext relatedContext = telemetryContext.Clone();

0 commit comments

Comments
 (0)