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 @@ -38,7 +38,7 @@ public void SetupTests()
["Port"] = 6379,
["ServerInstances"] = 1,
["ServerThreadCount"] = 4,
["IsTLSEnabled"] = false
["IsTLSEnabled"] = "no"
};

string agentId = $"{Environment.MachineName}-Server";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace VirtualClient.Actions
{
using System;
using System.Collections.Generic;
using System.IO.Packaging;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
Expand Down Expand Up @@ -101,6 +102,28 @@ public bool WarmUp
}
}

/// <summary>
/// yes if TLS is enabled.
/// </summary>
public string IsTLSEnabled
{
get
{
return this.Parameters.GetValue<string>(nameof(this.IsTLSEnabled), "no");
}
}

/// <summary>
/// Parameter defines the number of server instances/copies to run.
/// </summary>
public string RedisResourcesPackageName
{
get
{
return this.Parameters.GetValue<string>(nameof(this.RedisResourcesPackageName));
}
}

/// <summary>
/// The benchmark target server (e.g. Redis, Memcached).
/// </summary>
Expand Down Expand Up @@ -132,6 +155,11 @@ public bool WarmUp
/// </summary>
protected string MemtierPackagePath { get; set; }

/// <summary>
/// Path to Redis resources.
/// </summary>
protected string RedisResourcesPath { get; set; }

/// <summary>
/// The timespan at which the client will poll the server for responses before
/// timing out.
Expand Down Expand Up @@ -230,6 +258,12 @@ protected override async Task InitializeAsync(EventContext telemetryContext, Can
this.Benchmark = "Redis";
}

if (string.Equals(this.IsTLSEnabled, "yes", StringComparison.OrdinalIgnoreCase))
{
DependencyPath redisResourcesPath = await this.GetPackageAsync(this.RedisResourcesPackageName, cancellationToken);
this.RedisResourcesPath = redisResourcesPath.Path;
}

await this.SystemManagement.MakeFileExecutableAsync(this.MemtierExecutablePath, this.Platform, cancellationToken);
this.InitializeApiClients();
}
Expand Down Expand Up @@ -297,6 +331,7 @@ private Task ExecuteWorkloadsAsync(IPAddress serverIPAddress, ServerState server
{
string command = this.MemtierExecutablePath;
string workingDirectory = this.MemtierPackagePath;
string commandArguments = string.Empty;
List<string> commands = new List<string>();

relatedContext.AddContext("command", command);
Expand All @@ -311,8 +346,15 @@ private Task ExecuteWorkloadsAsync(IPAddress serverIPAddress, ServerState server
// memtier_benchmark Documentation:
// https://github.com/RedisLabs/memtier_benchmark

string commandArguments = commandArguments = $"--server {serverIPAddress} --port {serverPort} {this.CommandLine}";

if (string.Equals(this.IsTLSEnabled, "yes", StringComparison.OrdinalIgnoreCase))
{
commandArguments = $"--server {serverIPAddress} --port {serverPort} --tls --cert {this.PlatformSpecifics.Combine(this.RedisResourcesPath, "redis.crt")} --key {this.PlatformSpecifics.Combine(this.RedisResourcesPath, "redis.key")} --cacert {this.PlatformSpecifics.Combine(this.RedisResourcesPath, "ca.crt")} {this.CommandLine}";
}
else
{
commandArguments = $"--server {serverIPAddress} --port {serverPort} {this.CommandLine}";
}

commands.Add(commandArguments);
workloadProcesses.Add(this.ExecuteWorkloadAsync(serverPort, command, commandArguments, workingDirectory, relatedContext, cancellationToken));

Expand Down Expand Up @@ -416,16 +458,9 @@ private string ParseCommand(string command)
{
@".*\/memtier_benchmark",
@"--port\s+\d+",
@"--protocol\s+\w+",
@"--key-prefix\s+\w+",
@"--test-time\s+\d+",
@"--key-minimum\s+\d+",
@"--key-maximum\s+\d+",
@"--key-prefix\s+\w+",
@"--key-pattern\s+\w+:\w+",
@"--run-count\s+\d+",
@"--print-percentiles\s+(?:\d{1,2}(?:\.\d+)?(?:,\d{1,2}(?:\.\d+)?)*)+",
@"--tls",
@"--key-prefix\s+\w+",
@"--print-percentiles\s+(?:\d{1,2}(?:\.\d+)?(?:,\d{1,2}(?:\.\d+)?)*)+",
@"--cert\s+.*\.crt",
@"--key\s+.*\.key",
@"--cacert\s+.*\.crt",
Expand All @@ -436,9 +471,7 @@ private string ParseCommand(string command)
command = Regex.Replace(command, regexPattern, string.Empty);
}

command = Regex.Replace(command, @"\s+", " "); // Remove extra spaces

Console.WriteLine($"final command without performance affecting values {command.Trim()}");
command = Regex.Replace(command, @"\s+", " "); // Removes extra spaces

return command.Trim();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace VirtualClient.Actions
{
using System;
using System.Collections.Generic;
using System.IO.Packaging;
using System.Linq;
using System.Net;
using System.Net.Http;
Expand Down Expand Up @@ -77,11 +78,11 @@ public int Port
/// <summary>
/// True if TLS is enabled.
/// </summary>
public bool IsTLSEnabled
public string IsTLSEnabled
{
get
{
return this.Parameters.GetValue<bool>(nameof(this.IsTLSEnabled));
return this.Parameters.GetValue<string>(nameof(this.IsTLSEnabled), "no");
}
}

Expand All @@ -96,6 +97,17 @@ public int ServerInstances
}
}

/// <summary>
/// Parameter defines the number of server instances/copies to run.
/// </summary>
public string RedisResourcesPackageName
{
get
{
return this.Parameters.GetValue<string>(nameof(this.RedisResourcesPackageName));
}
}

/// <summary>
/// Client used to communicate with the locally hosted instance of the
/// Virtual Client API.
Expand All @@ -113,6 +125,11 @@ protected IApiClient ApiClient
/// </summary>
protected string RedisExecutablePath { get; set; }

/// <summary>
/// Path to Redis resources.
/// </summary>
protected string RedisResourcesPath { get; set; }

/// <summary>
/// Path to Redis server package.
/// </summary>
Expand Down Expand Up @@ -215,6 +232,12 @@ protected override async Task InitializeAsync(EventContext telemetryContext, Can

await this.SystemManagement.MakeFileExecutableAsync(this.RedisExecutablePath, this.Platform, cancellationToken);

if (string.Equals(this.IsTLSEnabled, "yes", StringComparison.OrdinalIgnoreCase))
{
DependencyPath redisResourcesPath = await this.GetPackageAsync(this.RedisResourcesPackageName, cancellationToken);
this.RedisResourcesPath = redisResourcesPath.Path;
}

this.InitializeApiClients();
}

Expand Down Expand Up @@ -350,9 +373,9 @@ private void StartServerInstances(EventContext telemetryContext, CancellationTok
commandArguments = $"-c \"{this.RedisExecutablePath}";
}

if (this.IsTLSEnabled)
if (string.Equals(this.IsTLSEnabled, "yes", StringComparison.OrdinalIgnoreCase))
{
commandArguments += $" --tls-port {port} --port 0";
commandArguments += $" --tls-port {port} --port 0 --tls-cert-file {this.PlatformSpecifics.Combine(this.RedisResourcesPath, "redis.crt")} --tls-key-file {this.PlatformSpecifics.Combine(this.RedisResourcesPath, "redis.key")} --tls-ca-cert-file {this.PlatformSpecifics.Combine(this.RedisResourcesPath, "ca.crt")}";
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace VirtualClient.Dependencies
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using NUnit.Framework;
using VirtualClient.Common.Telemetry;
using VirtualClient.Contracts;

[TestFixture]
[Category("Unit")]
public class RedisPackageInstallationTests
{
private MockFixture mockFixture;
private DependencyPath mockPackage;

[SetUp]
public void SetupTest()
{
this.mockFixture = new MockFixture();
}

[Test]
public void RedisPackageInstallationThrowsIfDistroNotSupportedForLinux()
{
this.SetupDefaultMockBehavior();
LinuxDistributionInfo mockInfo = new LinuxDistributionInfo()
{
OperationSystemFullName = "TestUbuntu",
LinuxDistribution = LinuxDistribution.SUSE
};

this.mockFixture.SystemManagement.Setup(sm => sm.GetLinuxDistributionAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(mockInfo);

using (TestRedisPackageInstallation installation = new TestRedisPackageInstallation(this.mockFixture.Dependencies, this.mockFixture.Parameters))
{
WorkloadException exception = Assert.ThrowsAsync<WorkloadException>(() => installation.ExecuteAsync(CancellationToken.None));
Assert.AreEqual(ErrorReason.LinuxDistributionNotSupported, exception.Reason);
}
}

[Test]
[TestCase(Architecture.X64, "linux-x64")]
[TestCase(Architecture.Arm64, "linux-arm64")]
public async Task RedisPackageInstallationExecutesExpectedInstallationCommandsOnUbuntu(Architecture architecture, string platformArchitecture)
{
this.SetupDefaultMockBehavior(PlatformID.Unix, architecture);

LinuxDistributionInfo mockInfo = new LinuxDistributionInfo()
{
OperationSystemFullName = "TestUbuntu",
LinuxDistribution = LinuxDistribution.Ubuntu
};

this.mockFixture.SystemManagement.Setup(sm => sm.GetLinuxDistributionAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(mockInfo);

using (TestRedisPackageInstallation installation = new TestRedisPackageInstallation(this.mockFixture.Dependencies, this.mockFixture.Parameters))
{
await installation.ExecuteAsync(CancellationToken.None);

Assert.IsTrue(this.mockFixture.ProcessManager.CommandsExecuted($"apt update"));
Assert.IsTrue(this.mockFixture.ProcessManager.CommandsExecuted($"apt install redis -y"));
}
}

private void SetupDefaultMockBehavior(PlatformID platform = PlatformID.Unix, Architecture architecture = Architecture.X64)
{
this.mockFixture.Setup(platform, architecture);
this.mockFixture.Parameters = new Dictionary<string, IConvertible>()
{
{ "PackageName", "redis" }
};

this.mockPackage = new DependencyPath("redis", this.mockFixture.GetPackagePath("redis"));
this.mockFixture.FileSystem.Setup(fe => fe.File.Exists(It.IsAny<string>())).Returns(true);
this.mockFixture.PackageManager.OnGetPackage().ReturnsAsync(this.mockPackage);
}

private class TestRedisPackageInstallation : RedisPackageInstallation
{
public TestRedisPackageInstallation(IServiceCollection dependencies, IDictionary<string, IConvertible> parameters)
: base(dependencies, parameters)
{
}

public new Task InitializeAsync(EventContext context, CancellationToken cancellationToken)
{
return base.InitializeAsync(context, cancellationToken);
}

public new Task ExecuteAsync(EventContext context, CancellationToken cancellationToken)
{
return base.ExecuteAsync(context, cancellationToken);
}
}
}
}
Loading