Skip to content

Commit eee8cba

Browse files
authored
Recording redis version in the executor (#526)
1 parent 77f3f13 commit eee8cba

File tree

3 files changed

+136
-4
lines changed

3 files changed

+136
-4
lines changed

src/VirtualClient/VirtualClient.Actions.FunctionalTests/Redis/RedisServerProfileTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace VirtualClient.Actions
88
using System.Linq;
99
using System.Net;
1010
using System.Runtime.InteropServices;
11+
using System.Text;
1112
using System.Threading;
1213
using System.Threading.Tasks;
1314
using Moq;
@@ -44,6 +45,24 @@ public void SetupFixture()
4445
[TestCase("PERF-REDIS.json")]
4546
public async Task RedisMemtierWorkloadProfileInstallsTheExpectedDependenciesOfServerOnUnixPlatform(string profile)
4647
{
48+
using var memoryProcess = new InMemoryProcess
49+
{
50+
StandardOutput = new ConcurrentBuffer(
51+
new StringBuilder("Redis server v=7.0.15 sha=00000000 malloc=jemalloc-5.1.0 bits=64 build=abc123")),
52+
OnStart = () => true,
53+
OnHasExited = () => true
54+
};
55+
56+
this.mockFixture.ProcessManager.OnCreateProcess = (command, arguments, workingDir) =>
57+
{
58+
IProcessProxy process = this.mockFixture.CreateProcess(command, arguments, workingDir);
59+
if (arguments?.Contains("redis-server") == true && arguments?.Contains("--version") == true)
60+
{
61+
return memoryProcess;
62+
}
63+
64+
return process;
65+
};
4766
using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
4867
{
4968
await executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None)
@@ -86,10 +105,21 @@ public async Task RedisMemtierWorkloadProfileExecutesTheWorkloadAsExpectedOfServ
86105
});
87106

88107
await apiClient.CreateStateAsync(nameof(ServerState), state, CancellationToken.None);
108+
using var memoryProcess = new InMemoryProcess
109+
{
110+
StandardOutput = new ConcurrentBuffer(
111+
new StringBuilder("Redis server v=7.0.15 sha=00000000 malloc=jemalloc-5.1.0 bits=64 build=abc123")),
112+
OnStart = () => true,
113+
OnHasExited = () => true
114+
};
89115

90116
this.mockFixture.ProcessManager.OnCreateProcess = (command, arguments, workingDir) =>
91117
{
92118
IProcessProxy process = this.mockFixture.CreateProcess(command, arguments, workingDir);
119+
if (arguments?.Contains("redis-server") == true && arguments?.Contains("--version") == true)
120+
{
121+
return memoryProcess;
122+
}
93123

94124
return process;
95125
};

src/VirtualClient/VirtualClient.Actions.UnitTests/Redis/RedisServerExecutorTests.cs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ namespace VirtualClient.Actions
55
{
66
using System;
77
using System.Collections.Generic;
8+
using System.Linq;
89
using System.Net;
10+
using System.Text;
911
using System.Threading;
1012
using System.Threading.Tasks;
1113
using Microsoft.Extensions.DependencyInjection;
1214
using Moq;
1315
using NUnit.Framework;
1416
using VirtualClient.Actions.Memtier;
17+
using VirtualClient.Common;
1518
using VirtualClient.Common.Telemetry;
1619
using VirtualClient.Contracts;
1720

@@ -21,13 +24,19 @@ public class RedisServerExecutorTests
2124
{
2225
private MockFixture fixture;
2326
private DependencyPath mockRedisPackage;
27+
private InMemoryProcess memoryProcess;
2428

2529
[SetUp]
2630
public void SetupTests()
2731
{
2832
this.fixture = new MockFixture();
2933
this.fixture.Setup(PlatformID.Unix);
30-
34+
this.memoryProcess = new InMemoryProcess
35+
{
36+
ExitCode = 0,
37+
OnStart = () => true,
38+
OnHasExited = () => true
39+
};
3140
this.mockRedisPackage = new DependencyPath("redis", this.fixture.GetPackagePath("redis"));
3241

3342
this.fixture.Parameters = new Dictionary<string, IConvertible>()
@@ -62,6 +71,17 @@ public async Task RedisServerExecutorConfirmsTheExpectedPackagesOnInitialization
6271
{
6372
using (var component = new TestRedisServerExecutor(this.fixture.Dependencies, this.fixture.Parameters))
6473
{
74+
this.fixture.ProcessManager.OnCreateProcess = (command, arguments, workingDirectory) =>
75+
{
76+
if (arguments?.Contains("redis-server") == true && arguments?.Contains("--version") == true)
77+
{
78+
this.memoryProcess.StandardOutput = new ConcurrentBuffer(
79+
new StringBuilder("Redis server v=7.0.15 sha=00000000 malloc=jemalloc-5.1.0 bits=64 build=abc123")
80+
);
81+
return this.memoryProcess;
82+
}
83+
return this.memoryProcess;
84+
};
6585
await component.InitializeAsync(EventContext.None, CancellationToken.None);
6686
this.fixture.PackageManager.Verify(mgr => mgr.GetPackageAsync(this.mockRedisPackage.Name, It.IsAny<CancellationToken>()));
6787
}
@@ -83,6 +103,13 @@ public async Task RedisMemtierServerExecutorExecutesExpectedProcessWhenBindingTo
83103

84104
this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDirectory) =>
85105
{
106+
if (arguments?.Contains("redis-server") == true && arguments?.Contains("--version") == true)
107+
{
108+
this.memoryProcess.StandardOutput = new ConcurrentBuffer(
109+
new StringBuilder("Redis server v=7.0.15 sha=00000000 malloc=jemalloc-5.1.0 bits=64 build=abc123")
110+
);
111+
return this.memoryProcess;
112+
}
86113
expectedCommands.Remove($"{exe} {arguments}");
87114
return this.fixture.Process;
88115
};
@@ -113,6 +140,13 @@ public async Task RedisMemtierServerExecutorExecutesExpectedProcessWhenBindingTo
113140

114141
this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDirectory) =>
115142
{
143+
if (arguments?.Contains("redis-server") == true && arguments?.Contains("--version") == true)
144+
{
145+
this.memoryProcess.StandardOutput = new ConcurrentBuffer(
146+
new StringBuilder("Redis server v=7.0.15 sha=00000000 malloc=jemalloc-5.1.0 bits=64 build=abc123")
147+
);
148+
return this.memoryProcess;
149+
}
116150
expectedCommands.Remove($"{exe} {arguments}");
117151
return this.fixture.Process;
118152
};
@@ -140,6 +174,13 @@ public async Task RedisMemtierServerExecutorExecutesExpectedProcessWhenNotBindin
140174

141175
this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDirectory) =>
142176
{
177+
if (arguments?.Contains("redis-server") == true && arguments?.Contains("--version") == true)
178+
{
179+
this.memoryProcess.StandardOutput = new ConcurrentBuffer(
180+
new StringBuilder("Redis server v=7.0.15 sha=00000000 malloc=jemalloc-5.1.0 bits=64 build=abc123")
181+
);
182+
return this.memoryProcess;
183+
}
143184
expectedCommands.Remove($"{exe} {arguments}");
144185
return this.fixture.Process;
145186
};
@@ -149,6 +190,38 @@ public async Task RedisMemtierServerExecutorExecutesExpectedProcessWhenNotBindin
149190
}
150191
}
151192

193+
[Test]
194+
public async Task RedisServerExecutorCapturesRedisVersionSuccessfully()
195+
{
196+
using (var executor = new TestRedisServerExecutor(this.fixture.Dependencies, this.fixture.Parameters))
197+
{
198+
this.fixture.ProcessManager.OnCreateProcess = (command, arguments, workingDirectory) =>
199+
{
200+
if (arguments?.Contains("redis-server") == true && arguments?.Contains("--version") == true)
201+
{
202+
this.memoryProcess.StandardOutput = new ConcurrentBuffer(
203+
new StringBuilder("Redis server v=7.0.15 sha=00000000 malloc=jemalloc-5.1.0 bits=64 build=abc123")
204+
);
205+
return this.memoryProcess;
206+
}
207+
return this.memoryProcess;
208+
};
209+
// Act
210+
await executor.InitializeAsync(EventContext.None, CancellationToken.None);
211+
// Assert
212+
var messages = this.fixture.Logger.MessagesLogged($"{nameof(TestRedisServerExecutor)}.RedisVersionCaptured");
213+
Assert.IsNotEmpty(messages, "Expected at least one log message indicating the Redis version was captured.");
214+
bool versionCapturedCorrectly = messages.Any(msg =>
215+
{
216+
var eventContext = msg.Item3 as EventContext;
217+
return eventContext != null &&
218+
eventContext.Properties.ContainsKey("redisVersion") &&
219+
eventContext.Properties["redisVersion"].ToString() == "7.0.15";
220+
});
221+
Assert.IsTrue(versionCapturedCorrectly, "The Redis version '7.0.15' was not captured correctly in the logs.");
222+
}
223+
}
224+
152225
private class TestRedisServerExecutor : RedisServerExecutor
153226
{
154227
public TestRedisServerExecutor(IServiceCollection services, IDictionary<string, IConvertible> parameters = null)

src/VirtualClient/VirtualClient.Actions/Redis/RedisServerExecutor.cs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace VirtualClient.Actions
88
using System.Linq;
99
using System.Net;
1010
using System.Net.Http;
11+
using System.Text.RegularExpressions;
1112
using System.Threading;
1213
using System.Threading.Tasks;
1314
using Microsoft.Extensions.DependencyInjection;
@@ -19,6 +20,7 @@ namespace VirtualClient.Actions
1920
using VirtualClient.Common.Extensions;
2021
using VirtualClient.Common.Telemetry;
2122
using VirtualClient.Contracts;
23+
using VirtualClient.Contracts.Metadata;
2224
using VirtualClient.Logging;
2325

2426
/// <summary>
@@ -142,6 +144,8 @@ protected IApiClient ApiClient
142144
/// </summary>
143145
protected IAsyncPolicy ServerRetryPolicy { get; set; }
144146

147+
private string RedisVersion { get; set; }
148+
145149
/// <summary>
146150
/// Disposes of resources used by the executor including shutting down any
147151
/// instances of Redis server running.
@@ -195,7 +199,6 @@ protected override Task ExecuteAsync(EventContext telemetryContext, Cancellation
195199

196200
await this.SaveStateAsync(telemetryContext, cancellationToken);
197201
this.SetServerOnline(true);
198-
199202
if (this.IsMultiRoleLayout())
200203
{
201204
using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
@@ -237,13 +240,13 @@ protected override async Task InitializeAsync(EventContext telemetryContext, Can
237240
this.RedisExecutablePath = this.Combine(this.RedisPackagePath, "src", "redis-server");
238241

239242
await this.SystemManagement.MakeFileExecutableAsync(this.RedisExecutablePath, this.Platform, cancellationToken);
240-
243+
await this.CaptureRedisVersionAsync(telemetryContext, cancellationToken);
241244
if (this.IsTLSEnabled)
242245
{
243246
DependencyPath redisResourcesPath = await this.GetPackageAsync(this.RedisResourcesPackageName, cancellationToken);
244247
this.RedisResourcesPath = redisResourcesPath.Path;
245248
}
246-
249+
247250
this.InitializeApiClients();
248251
}
249252

@@ -264,6 +267,32 @@ protected override void Validate()
264267
}
265268
}
266269

270+
private async Task CaptureRedisVersionAsync(EventContext telemetryContext, CancellationToken token)
271+
{
272+
try
273+
{
274+
string command = $"{this.RedisExecutablePath} --version";
275+
IProcessProxy process = await this.ExecuteCommandAsync(command, null, this.RedisPackagePath, telemetryContext, token, runElevated: true);
276+
string output = process.StandardOutput.ToString();
277+
Match match = Regex.Match(output, @"v=(\d+\.\d+\.\d+)");
278+
this.RedisVersion = match.Success ? match.Groups[1].Value : null;
279+
if (!string.IsNullOrEmpty(this.RedisVersion))
280+
{
281+
telemetryContext.AddContext("RedisVersion", this.RedisVersion);
282+
this.Logger.LogMessage($"{this.TypeName}.RedisVersionCaptured", LogLevel.Information, telemetryContext);
283+
this.MetadataContract.AddForScenario(
284+
"Redis-Benchmark",
285+
null,
286+
toolVersion: this.RedisVersion);
287+
this.MetadataContract.Apply(telemetryContext);
288+
}
289+
}
290+
catch (Exception ex)
291+
{
292+
this.Logger.LogErrorMessage(ex, telemetryContext);
293+
}
294+
}
295+
267296
private Task DeleteStateAsync(EventContext telemetryContext, CancellationToken cancellationToken)
268297
{
269298
EventContext relatedContext = telemetryContext.Clone();

0 commit comments

Comments
 (0)