From 0d4960f3ff3217a37a1b07d5bac441730e8237ff Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 30 Jun 2023 09:09:59 +0200 Subject: [PATCH] cleanup help cli args. Adds option to stop tests on first failure and download cluster logs. --- ArgsUniform/ArgsUniform.cs | 23 ++++++++++++++++------- ArgsUniform/ExampleUser.cs | 7 ++++++- CodexNetDeployer/Program.cs | 11 +---------- CodexNetDownloader/Program.cs | 13 ++----------- ContinuousTests/Configuration.cs | 20 +++++++++++++++++++- ContinuousTests/ContinuousTestRunner.cs | 2 +- ContinuousTests/K8sFactory.cs | 4 ++-- ContinuousTests/NodeRunner.cs | 2 +- ContinuousTests/Program.cs | 15 ++++++++++++--- ContinuousTests/SingleTestRun.cs | 22 +++++++++++++++++++++- 10 files changed, 81 insertions(+), 38 deletions(-) diff --git a/ArgsUniform/ArgsUniform.cs b/ArgsUniform/ArgsUniform.cs index 7d58cf1e..88e539a7 100644 --- a/ArgsUniform/ArgsUniform.cs +++ b/ArgsUniform/ArgsUniform.cs @@ -4,6 +4,7 @@ namespace ArgsUniform { public class ArgsUniform { + private readonly Action printAppInfo; private readonly object? defaultsProvider; private readonly IEnv.IEnv env; private readonly string[] args; @@ -12,23 +13,24 @@ namespace ArgsUniform private const int envStart = 48; private const int descStart = 80; - public ArgsUniform(params string[] args) - : this(new IEnv.Env(), args) + public ArgsUniform(Action printAppInfo, params string[] args) + : this(printAppInfo, new IEnv.Env(), args) { } - public ArgsUniform(object defaultsProvider, params string[] args) - : this(defaultsProvider, new IEnv.Env(), args) + public ArgsUniform(Action printAppInfo, object defaultsProvider, params string[] args) + : this(printAppInfo, defaultsProvider, new IEnv.Env(), args) { } - public ArgsUniform(IEnv.IEnv env, params string[] args) - : this(null!, env, args) + public ArgsUniform(Action printAppInfo, IEnv.IEnv env, params string[] args) + : this(printAppInfo, null!, env, args) { } - public ArgsUniform(object defaultsProvider, IEnv.IEnv env, params string[] args) + public ArgsUniform(Action printAppInfo, object defaultsProvider, IEnv.IEnv env, params string[] args) { + this.printAppInfo = printAppInfo; this.defaultsProvider = defaultsProvider; this.env = env; this.args = args; @@ -36,6 +38,13 @@ namespace ArgsUniform public T Parse(bool printResult = false) { + if (args.Any(a => a == "-h" || a == "--help" || a == "-?")) + { + printAppInfo(); + PrintHelp(); + throw new Exception(); + } + var result = Activator.CreateInstance(); var uniformProperties = typeof(T).GetProperties().Where(m => m.GetCustomAttributes(typeof(UniformAttribute), false).Length == 1).ToArray(); var missingRequired = new List(); diff --git a/ArgsUniform/ExampleUser.cs b/ArgsUniform/ExampleUser.cs index f8b57876..31db9ab7 100644 --- a/ArgsUniform/ExampleUser.cs +++ b/ArgsUniform/ExampleUser.cs @@ -21,9 +21,14 @@ // env var: "AAA=BBB" var args = "--ccc=ddd"; - var uniform = new ArgsUniform(new DefaultsProvider(), args); + var uniform = new ArgsUniform(PrintHelp, new DefaultsProvider(), args); var aaa = uniform.Parse(); } + + private static void PrintHelp() + { + Console.WriteLine("Help text!"); + } } } diff --git a/CodexNetDeployer/Program.cs b/CodexNetDeployer/Program.cs index 4ac3a2f8..b50c1594 100644 --- a/CodexNetDeployer/Program.cs +++ b/CodexNetDeployer/Program.cs @@ -11,13 +11,7 @@ public class Program var nl = Environment.NewLine; Console.WriteLine("CodexNetDeployer" + nl); - if (args.Any(a => a == "-h" || a == "--help" || a == "-?")) - { - PrintHelp(); - return; - } - - var uniformArgs = new ArgsUniform(args); + var uniformArgs = new ArgsUniform(PrintHelp, args); var config = uniformArgs.Parse(true); if (args.Any(a => a == "--external")) @@ -54,8 +48,5 @@ public class Program Console.WriteLine("CodexNetDeployer assumes you are running this tool from *inside* the Kubernetes cluster you want to deploy to. " + "If you are not running this from a container inside the cluster, add the argument '--external'." + nl); - - var uniformArgs = new ArgsUniform(); - uniformArgs.PrintHelp(); } } diff --git a/CodexNetDownloader/Program.cs b/CodexNetDownloader/Program.cs index d17d76ad..f52a1d27 100644 --- a/CodexNetDownloader/Program.cs +++ b/CodexNetDownloader/Program.cs @@ -12,13 +12,7 @@ public class Program var nl = Environment.NewLine; Console.WriteLine("CodexNetDownloader" + nl); - if (args.Any(a => a == "-h" || a == "--help" || a == "-?")) - { - PrintHelp(); - return; - } - - var uniformArgs = new ArgsUniform(args); + var uniformArgs = new ArgsUniform(PrintHelp, args); var config = uniformArgs.Parse(true); if (args.Any(a => a == "--external")) @@ -31,7 +25,7 @@ public class Program if (!Directory.Exists(config.OutputPath)) Directory.CreateDirectory(config.OutputPath); var k8sFactory = new K8sFactory(); - var (_, lifecycle) = k8sFactory.CreateFacilities(config.KubeConfigFile, config.OutputPath, "dataPath", config.CodexDeployment.Metadata.KubeNamespace, new DefaultTimeSet(), new NullLog()); + var (_, lifecycle) = k8sFactory.CreateFacilities(config.KubeConfigFile, config.OutputPath, "dataPath", config.CodexDeployment.Metadata.KubeNamespace, new DefaultTimeSet(), new NullLog(), config.RunnerLocation); foreach (var container in config.CodexDeployment.CodexContainers) { @@ -55,8 +49,5 @@ public class Program Console.WriteLine("CodexNetDownloader assumes you are running this tool from *inside* the Kubernetes cluster. " + "If you are not running this from a container inside the cluster, add the argument '--external'." + nl); - - var uniformArgs = new ArgsUniform(); - uniformArgs.PrintHelp(); } } diff --git a/ContinuousTests/Configuration.cs b/ContinuousTests/Configuration.cs index 1d6b6352..19a2bf63 100644 --- a/ContinuousTests/Configuration.cs +++ b/ContinuousTests/Configuration.cs @@ -21,18 +21,27 @@ namespace ContinuousTests [Uniform("kube-config", "kc", "KUBECONFIG", true, "Path to Kubeconfig file. Use 'null' (default) to use local cluster.")] public string KubeConfigFile { get; set; } = "null"; + [Uniform("stop", "s", "STOPONFAIL", false, "If true, runner will stop on first test failure and download all cluster container logs. False by default.")] + public bool StopOnFailure { get; set; } = false; + public CodexDeployment CodexDeployment { get; set; } = null!; + + public TestRunnerLocation RunnerLocation { get; set; } = TestRunnerLocation.InternalToCluster; } public class ConfigLoader { public Configuration Load(string[] args) { - var uniformArgs = new ArgsUniform(args); + var uniformArgs = new ArgsUniform(PrintHelp, args); var result = uniformArgs.Parse(true); result.CodexDeployment = ParseCodexDeploymentJson(result.CodexDeploymentJson); + if (args.Any(a => a == "--external")) + { + result.RunnerLocation = TestRunnerLocation.ExternalToCluster; + } return result; } @@ -43,5 +52,14 @@ namespace ContinuousTests if (d == null) throw new Exception("Unable to parse " + filename); return d; } + + private static void PrintHelp() + { + var nl = Environment.NewLine; + Console.WriteLine("CodexNetDownloader lets you download all container logs given a codex-deployment.json file." + nl); + + Console.WriteLine("CodexNetDownloader assumes you are running this tool from *inside* the Kubernetes cluster. " + + "If you are not running this from a container inside the cluster, add the argument '--external'." + nl); + } } } diff --git a/ContinuousTests/ContinuousTestRunner.cs b/ContinuousTests/ContinuousTestRunner.cs index 7ad16faf..0e699e16 100644 --- a/ContinuousTests/ContinuousTestRunner.cs +++ b/ContinuousTests/ContinuousTestRunner.cs @@ -58,7 +58,7 @@ namespace ContinuousTests if (string.IsNullOrEmpty(test.CustomK8sNamespace)) return; log.Log($"Clearing namespace '{test.CustomK8sNamespace}'..."); - var (workflowCreator, _) = k8SFactory.CreateFacilities(config.KubeConfigFile, config.LogPath, config.DataPath, test.CustomK8sNamespace, new DefaultTimeSet(), log); + var (workflowCreator, _) = k8SFactory.CreateFacilities(config.KubeConfigFile, config.LogPath, config.DataPath, test.CustomK8sNamespace, new DefaultTimeSet(), log, config.RunnerLocation); workflowCreator.CreateWorkflow().DeleteTestResources(); } } diff --git a/ContinuousTests/K8sFactory.cs b/ContinuousTests/K8sFactory.cs index 221e1123..0704e9f1 100644 --- a/ContinuousTests/K8sFactory.cs +++ b/ContinuousTests/K8sFactory.cs @@ -7,7 +7,7 @@ namespace ContinuousTests { public class K8sFactory { - public (WorkflowCreator, TestLifecycle) CreateFacilities(string kubeConfigFile, string logPath, string dataFilePath, string customNamespace, ITimeSet timeSet, BaseLog log) + public (WorkflowCreator, TestLifecycle) CreateFacilities(string kubeConfigFile, string logPath, string dataFilePath, string customNamespace, ITimeSet timeSet, BaseLog log, TestRunnerLocation runnerLocation) { var kubeConfig = GetKubeConfig(kubeConfigFile); var lifecycleConfig = new DistTestCore.Configuration @@ -17,7 +17,7 @@ namespace ContinuousTests logDebug: false, dataFilesPath: dataFilePath, codexLogLevel: CodexLogLevel.Debug, - runnerLocation: TestRunnerLocation.ExternalToCluster + runnerLocation: runnerLocation ); var kubeFlowConfig = new KubernetesWorkflow.Configuration( diff --git a/ContinuousTests/NodeRunner.cs b/ContinuousTests/NodeRunner.cs index b970a08b..02116ee6 100644 --- a/ContinuousTests/NodeRunner.cs +++ b/ContinuousTests/NodeRunner.cs @@ -91,7 +91,7 @@ namespace ContinuousTests private (WorkflowCreator, TestLifecycle) CreateFacilities() { - return k8SFactory.CreateFacilities(config.KubeConfigFile, config.LogPath, config.DataPath, customNamespace, timeSet, log); + return k8SFactory.CreateFacilities(config.KubeConfigFile, config.LogPath, config.DataPath, customNamespace, timeSet, log, config.RunnerLocation); } } } diff --git a/ContinuousTests/Program.cs b/ContinuousTests/Program.cs index 848e02c0..273f13a1 100644 --- a/ContinuousTests/Program.cs +++ b/ContinuousTests/Program.cs @@ -7,18 +7,27 @@ public class Program Console.WriteLine("Codex Continous-Test-Runner."); Console.WriteLine("Running..."); - var cts = new CancellationTokenSource(); - var runner = new ContinuousTestRunner(args, cts.Token); + var runner = new ContinuousTestRunner(args, Cancellation.Cts.Token); Console.CancelKeyPress += (sender, e) => { Console.WriteLine("Stopping..."); e.Cancel = true; - cts.Cancel(); + Cancellation.Cts.Cancel(); }; runner.Run(); Console.WriteLine("Done."); } + + public static class Cancellation + { + static Cancellation() + { + Cts = new CancellationTokenSource(); + } + + public static CancellationTokenSource Cts { get; } + } } diff --git a/ContinuousTests/SingleTestRun.cs b/ContinuousTests/SingleTestRun.cs index b67a0f7d..2accc795 100644 --- a/ContinuousTests/SingleTestRun.cs +++ b/ContinuousTests/SingleTestRun.cs @@ -5,6 +5,7 @@ using Utils; using KubernetesWorkflow; using NUnit.Framework.Internal; using System.Reflection; +using static Program; namespace ContinuousTests { @@ -69,6 +70,14 @@ namespace ContinuousTests { fixtureLog.Error("Test run failed with exception: " + ex); fixtureLog.MarkAsFailed(); + + if (config.StopOnFailure) + { + OverviewLog("Configured to stop on first failure. Downloading cluster logs..."); + DownloadClusterLogs(); + OverviewLog("Log download finished. Cancelling test runner..."); + Cancellation.Cts.Cancel(); + } } } @@ -117,6 +126,17 @@ namespace ContinuousTests throw ex; } + private void DownloadClusterLogs() + { + var k8sFactory = new K8sFactory(); + var (_, lifecycle) = k8sFactory.CreateFacilities(config.KubeConfigFile, config.LogPath, "dataPath", config.CodexDeployment.Metadata.KubeNamespace, new DefaultTimeSet(), new NullLog(), config.RunnerLocation); + + foreach (var container in config.CodexDeployment.CodexContainers) + { + lifecycle.DownloadLog(container); + } + } + private Exception UnpackException(Exception exception) { if (exception is AggregateException a) @@ -192,7 +212,7 @@ namespace ContinuousTests private DistTestCore.Configuration CreateFileManagerConfiguration() { return new DistTestCore.Configuration(null, string.Empty, false, dataFolder, - CodexLogLevel.Error, TestRunnerLocation.ExternalToCluster); + CodexLogLevel.Error, config.RunnerLocation); } } }