2023-11-13 10:56:02 +00:00
|
|
|
|
using DistTestCore;
|
|
|
|
|
using DistTestCore.Logs;
|
2023-06-28 13:11:20 +00:00
|
|
|
|
using Logging;
|
2023-10-23 12:17:52 +00:00
|
|
|
|
using Newtonsoft.Json;
|
2023-09-27 09:33:54 +00:00
|
|
|
|
using Utils;
|
2023-12-20 14:56:03 +00:00
|
|
|
|
using TaskFactory = Utils.TaskFactory;
|
2023-06-25 09:06:47 +00:00
|
|
|
|
|
|
|
|
|
namespace ContinuousTests
|
2023-06-21 06:28:40 +00:00
|
|
|
|
{
|
2023-06-25 07:53:10 +00:00
|
|
|
|
public class ContinuousTestRunner
|
2023-06-21 06:28:40 +00:00
|
|
|
|
{
|
2023-09-20 11:33:58 +00:00
|
|
|
|
private readonly EntryPointFactory entryPointFactory = new EntryPointFactory();
|
2023-06-21 06:28:40 +00:00
|
|
|
|
private readonly ConfigLoader configLoader = new ConfigLoader();
|
2023-06-21 07:27:59 +00:00
|
|
|
|
private readonly TestFactory testFactory = new TestFactory();
|
2023-06-25 07:53:10 +00:00
|
|
|
|
private readonly Configuration config;
|
2023-06-28 14:19:37 +00:00
|
|
|
|
private readonly CancellationToken cancelToken;
|
2023-06-21 08:06:54 +00:00
|
|
|
|
|
2023-06-28 14:19:37 +00:00
|
|
|
|
public ContinuousTestRunner(string[] args, CancellationToken cancelToken)
|
2023-06-21 08:06:54 +00:00
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
config = configLoader.Load(args);
|
2023-06-28 14:19:37 +00:00
|
|
|
|
this.cancelToken = cancelToken;
|
2023-06-21 08:06:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-06-25 07:53:10 +00:00
|
|
|
|
public void Run()
|
2023-06-21 06:28:40 +00:00
|
|
|
|
{
|
2023-11-08 08:24:39 +00:00
|
|
|
|
var logConfig = new LogConfig(config.LogPath);
|
2023-09-28 07:39:15 +00:00
|
|
|
|
var startTime = DateTime.UtcNow;
|
|
|
|
|
|
2023-09-21 08:33:09 +00:00
|
|
|
|
var overviewLog = new LogSplitter(
|
2024-02-22 13:41:07 +00:00
|
|
|
|
new FixtureLog(logConfig, startTime, config.CodexDeployment.Id, "Overview"),
|
2023-09-21 08:33:09 +00:00
|
|
|
|
new ConsoleLog()
|
|
|
|
|
);
|
2024-02-22 13:41:07 +00:00
|
|
|
|
var statusLog = new StatusLog(logConfig, startTime, "continuous-tests", config.CodexDeployment.Id,
|
|
|
|
|
"ContinuousTestRun");
|
2023-09-21 08:33:09 +00:00
|
|
|
|
|
|
|
|
|
overviewLog.Log("Initializing...");
|
2023-09-20 11:33:58 +00:00
|
|
|
|
|
|
|
|
|
var entryPoint = entryPointFactory.CreateEntryPoint(config.KubeConfigFile, config.DataPath, config.CodexDeployment.Metadata.KubeNamespace, overviewLog);
|
|
|
|
|
entryPoint.Announce();
|
|
|
|
|
|
2023-09-21 08:33:09 +00:00
|
|
|
|
overviewLog.Log("Initialized. Performing startup checks...");
|
|
|
|
|
|
2023-09-20 11:33:58 +00:00
|
|
|
|
var startupChecker = new StartupChecker(entryPoint, config, cancelToken);
|
2023-06-25 07:53:10 +00:00
|
|
|
|
startupChecker.Check();
|
2023-06-21 06:28:40 +00:00
|
|
|
|
|
2023-06-28 14:19:37 +00:00
|
|
|
|
var taskFactory = new TaskFactory();
|
2023-10-23 12:17:52 +00:00
|
|
|
|
overviewLog.Log("Startup checks passed. Configuration:");
|
|
|
|
|
overviewLog.Log(JsonConvert.SerializeObject(config, Formatting.Indented));
|
2023-11-13 10:56:02 +00:00
|
|
|
|
overviewLog.Log("Test framework revision: " + GitInfo.GetStatus());
|
2023-10-23 12:17:52 +00:00
|
|
|
|
overviewLog.Log("Continuous tests starting...");
|
2023-09-21 08:33:09 +00:00
|
|
|
|
overviewLog.Log("");
|
2023-06-25 07:53:10 +00:00
|
|
|
|
var allTests = testFactory.CreateTests();
|
2023-06-28 13:11:20 +00:00
|
|
|
|
|
|
|
|
|
ClearAllCustomNamespaces(allTests, overviewLog);
|
|
|
|
|
|
2023-10-03 07:53:02 +00:00
|
|
|
|
var filteredTests = FilterTests(allTests, overviewLog);
|
|
|
|
|
if (!filteredTests.Any())
|
|
|
|
|
{
|
|
|
|
|
overviewLog.Log("No tests selected.");
|
2023-10-31 14:33:00 +00:00
|
|
|
|
Cancellation.Cts.Cancel();
|
2023-10-03 07:53:02 +00:00
|
|
|
|
}
|
2023-10-31 14:33:00 +00:00
|
|
|
|
else
|
2023-06-21 06:28:40 +00:00
|
|
|
|
{
|
2023-11-09 10:35:45 +00:00
|
|
|
|
var testLoops = filteredTests.Select(t => new TestLoop(entryPointFactory, taskFactory, config, overviewLog, statusLog, t.GetType(), t.RunTestEvery, startupChecker, cancelToken)).ToArray();
|
2023-06-28 14:19:37 +00:00
|
|
|
|
|
2023-10-31 14:33:00 +00:00
|
|
|
|
foreach (var testLoop in testLoops)
|
|
|
|
|
{
|
|
|
|
|
if (cancelToken.IsCancellationRequested) break;
|
2023-06-27 08:16:59 +00:00
|
|
|
|
|
2023-10-31 14:33:00 +00:00
|
|
|
|
overviewLog.Log("Launching test-loop for " + testLoop.Name);
|
|
|
|
|
testLoop.Begin();
|
|
|
|
|
Thread.Sleep(TimeSpan.FromSeconds(5));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
overviewLog.Log("Finished launching test-loops.");
|
|
|
|
|
WaitUntilFinished(overviewLog, statusLog, startTime, testLoops);
|
|
|
|
|
overviewLog.Log("Stopping all test-loops...");
|
|
|
|
|
}
|
2023-06-28 14:19:37 +00:00
|
|
|
|
taskFactory.WaitAll();
|
2023-06-29 08:23:04 +00:00
|
|
|
|
overviewLog.Log("All tasks cancelled.");
|
2023-10-03 07:53:02 +00:00
|
|
|
|
|
|
|
|
|
PerformCleanup(overviewLog);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ContinuousTest[] FilterTests(ContinuousTest[] allTests, ILog log)
|
|
|
|
|
{
|
|
|
|
|
log.Log($"Available tests: {string.Join(", ", allTests.Select(r => r.Name))}");
|
|
|
|
|
|
|
|
|
|
var result = allTests.ToArray();
|
|
|
|
|
var filters = config.Filter.Split(",", StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
if (filters.Any())
|
|
|
|
|
{
|
|
|
|
|
log.Log($"Applying filters: {string.Join(", ", filters)}");
|
|
|
|
|
result = allTests.Where(t => filters.Any(f => t.Name.Contains(f))).ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Log($"Selected for running: {string.Join(", ", result.Select(r => r.Name))}");
|
|
|
|
|
return result;
|
2023-06-23 09:38:30 +00:00
|
|
|
|
}
|
2023-06-28 13:11:20 +00:00
|
|
|
|
|
2023-09-28 07:39:15 +00:00
|
|
|
|
private void WaitUntilFinished(LogSplitter overviewLog, StatusLog statusLog, DateTime startTime, TestLoop[] testLoops)
|
2023-09-27 09:33:54 +00:00
|
|
|
|
{
|
2023-11-14 11:56:47 +00:00
|
|
|
|
var testDuration = (DateTime.UtcNow - startTime).TotalSeconds.ToString();
|
2023-09-28 07:39:15 +00:00
|
|
|
|
var testData = FormatTestRuns(testLoops);
|
2023-10-23 08:32:11 +00:00
|
|
|
|
overviewLog.Log("Total duration: " + testDuration);
|
2023-09-28 07:39:15 +00:00
|
|
|
|
|
2023-11-17 13:51:32 +00:00
|
|
|
|
if (!string.IsNullOrEmpty(config.TargetDurationSeconds))
|
2023-09-27 09:33:54 +00:00
|
|
|
|
{
|
2023-11-17 13:51:32 +00:00
|
|
|
|
var targetDuration = Time.ParseTimespan(config.TargetDurationSeconds);
|
2023-10-02 07:24:01 +00:00
|
|
|
|
var wasCancelled = cancelToken.WaitHandle.WaitOne(targetDuration);
|
|
|
|
|
if (!wasCancelled)
|
|
|
|
|
{
|
|
|
|
|
Cancellation.Cts.Cancel();
|
|
|
|
|
overviewLog.Log($"Congratulations! The targer duration has been reached! ({Time.FormatDuration(targetDuration)})");
|
|
|
|
|
statusLog.ConcludeTest("Passed", testDuration, testData);
|
2023-11-13 10:17:26 +00:00
|
|
|
|
Environment.ExitCode = 0;
|
2023-10-02 07:24:01 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-09-27 09:33:54 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cancelToken.WaitHandle.WaitOne();
|
2023-09-28 07:39:15 +00:00
|
|
|
|
}
|
2023-10-02 07:24:01 +00:00
|
|
|
|
statusLog.ConcludeTest("Failed", testDuration, testData);
|
2023-11-13 10:17:26 +00:00
|
|
|
|
Environment.ExitCode = 1;
|
2023-09-28 07:39:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Dictionary<string, string> FormatTestRuns(TestLoop[] testLoops)
|
|
|
|
|
{
|
|
|
|
|
var result = new Dictionary<string, string>();
|
|
|
|
|
foreach (var testLoop in testLoops)
|
|
|
|
|
{
|
2023-11-09 10:35:45 +00:00
|
|
|
|
result.Add("testname", testLoop.Name);
|
|
|
|
|
result.Add($"summary", $"passes: {testLoop.NumberOfPasses} - failures: {testLoop.NumberOfFailures}");
|
2023-09-27 09:33:54 +00:00
|
|
|
|
}
|
2023-09-28 07:39:15 +00:00
|
|
|
|
return result;
|
2023-09-27 09:33:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 08:33:09 +00:00
|
|
|
|
private void ClearAllCustomNamespaces(ContinuousTest[] allTests, ILog log)
|
2023-06-28 13:11:20 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (var test in allTests) ClearAllCustomNamespaces(test, log);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 08:33:09 +00:00
|
|
|
|
private void ClearAllCustomNamespaces(ContinuousTest test, ILog log)
|
2023-06-28 13:11:20 +00:00
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(test.CustomK8sNamespace)) return;
|
|
|
|
|
|
|
|
|
|
log.Log($"Clearing namespace '{test.CustomK8sNamespace}'...");
|
2023-09-20 11:33:58 +00:00
|
|
|
|
|
|
|
|
|
var entryPoint = entryPointFactory.CreateEntryPoint(config.KubeConfigFile, config.DataPath, test.CustomK8sNamespace, log);
|
|
|
|
|
entryPoint.Tools.CreateWorkflow().DeleteNamespacesStartingWith(test.CustomK8sNamespace);
|
2023-06-28 13:11:20 +00:00
|
|
|
|
}
|
2023-10-03 07:53:02 +00:00
|
|
|
|
|
|
|
|
|
private void PerformCleanup(ILog log)
|
|
|
|
|
{
|
|
|
|
|
if (!config.Cleanup) return;
|
|
|
|
|
log.Log("Cleaning up test namespace...");
|
|
|
|
|
|
|
|
|
|
var entryPoint = entryPointFactory.CreateEntryPoint(config.KubeConfigFile, config.DataPath, config.CodexDeployment.Metadata.KubeNamespace, log);
|
|
|
|
|
entryPoint.Decommission(deleteKubernetesResources: true, deleteTrackedFiles: true);
|
|
|
|
|
log.Log("Cleanup finished.");
|
|
|
|
|
}
|
2023-06-21 08:06:54 +00:00
|
|
|
|
}
|
2023-06-21 06:28:40 +00:00
|
|
|
|
}
|