Automatic downloading of container logs on test failure
This commit is contained in:
parent
ca5981e852
commit
a2e07fbd2e
|
@ -1,4 +1,5 @@
|
||||||
using Core;
|
using Core;
|
||||||
|
using KubernetesWorkflow;
|
||||||
|
|
||||||
namespace DistTestCore
|
namespace DistTestCore
|
||||||
{
|
{
|
||||||
|
@ -26,6 +27,11 @@ namespace DistTestCore
|
||||||
}
|
}
|
||||||
|
|
||||||
public KubernetesWorkflow.Configuration GetK8sConfiguration(ITimeSet timeSet, string k8sNamespace)
|
public KubernetesWorkflow.Configuration GetK8sConfiguration(ITimeSet timeSet, string k8sNamespace)
|
||||||
|
{
|
||||||
|
return GetK8sConfiguration(timeSet, new DoNothingK8sHooks(), k8sNamespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KubernetesWorkflow.Configuration GetK8sConfiguration(ITimeSet timeSet, IK8sHooks hooks, string k8sNamespace)
|
||||||
{
|
{
|
||||||
var config = new KubernetesWorkflow.Configuration(
|
var config = new KubernetesWorkflow.Configuration(
|
||||||
kubeConfigFile: kubeConfigFile,
|
kubeConfigFile: kubeConfigFile,
|
||||||
|
@ -35,6 +41,7 @@ namespace DistTestCore
|
||||||
);
|
);
|
||||||
|
|
||||||
config.AllowNamespaceOverride = false;
|
config.AllowNamespaceOverride = false;
|
||||||
|
config.Hooks = hooks;
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ namespace DistTestCore
|
||||||
{
|
{
|
||||||
WriteEndTestLog(lifecycle.Log);
|
WriteEndTestLog(lifecycle.Log);
|
||||||
|
|
||||||
IncludeLogsAndMetricsOnTestFailure(lifecycle);
|
IncludeLogsOnTestFailure(lifecycle);
|
||||||
lifecycle.DeleteAllResources();
|
lifecycle.DeleteAllResources();
|
||||||
lifecycle = null!;
|
lifecycle = null!;
|
||||||
});
|
});
|
||||||
|
@ -215,22 +215,21 @@ namespace DistTestCore
|
||||||
return testMethods.Any(m => m.GetCustomAttribute<UseLongTimeoutsAttribute>() != null);
|
return testMethods.Any(m => m.GetCustomAttribute<UseLongTimeoutsAttribute>() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void IncludeLogsAndMetricsOnTestFailure(TestLifecycle lifecycle)
|
private void IncludeLogsOnTestFailure(TestLifecycle lifecycle)
|
||||||
{
|
{
|
||||||
var result = TestContext.CurrentContext.Result;
|
var result = TestContext.CurrentContext.Result;
|
||||||
if (result.Outcome.Status == NUnit.Framework.Interfaces.TestStatus.Failed)
|
if (result.Outcome.Status == NUnit.Framework.Interfaces.TestStatus.Failed)
|
||||||
{
|
{
|
||||||
fixtureLog.MarkAsFailed();
|
fixtureLog.MarkAsFailed();
|
||||||
|
|
||||||
if (IsDownloadingLogsAndMetricsEnabled())
|
if (IsDownloadingLogsEnabled())
|
||||||
{
|
{
|
||||||
lifecycle.Log.Log("Downloading all CodexNode logs and metrics because of test failure...");
|
lifecycle.Log.Log("Downloading all container logs because of test failure...");
|
||||||
//DownloadAllLogs(lifecycle);
|
lifecycle.DownloadAllLogs();
|
||||||
//DownloadAllMetrics(lifecycle);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lifecycle.Log.Log("Skipping download of all CodexNode logs and metrics due to [DontDownloadLogsAndMetricsOnFailure] attribute.");
|
lifecycle.Log.Log("Skipping download of all container logs due to [DontDownloadLogsOnFailure] attribute.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,10 +244,10 @@ namespace DistTestCore
|
||||||
return TestContext.CurrentContext.Result.Outcome.Status.ToString();
|
return TestContext.CurrentContext.Result.Outcome.Status.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsDownloadingLogsAndMetricsEnabled()
|
private bool IsDownloadingLogsEnabled()
|
||||||
{
|
{
|
||||||
var testProperties = TestContext.CurrentContext.Test.Properties;
|
var testProperties = TestContext.CurrentContext.Test.Properties;
|
||||||
return !testProperties.ContainsKey(DontDownloadLogsAndMetricsOnFailureAttribute.DontDownloadKey);
|
return !testProperties.ContainsKey(DontDownloadLogsOnFailureAttribute.DontDownloadKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
namespace DistTestCore
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
|
||||||
public class DontDownloadLogsAndMetricsOnFailureAttribute : PropertyAttribute
|
|
||||||
{
|
|
||||||
public const string DontDownloadKey = "DontDownloadLogsAndMetrics";
|
|
||||||
|
|
||||||
public DontDownloadLogsAndMetricsOnFailureAttribute()
|
|
||||||
: base(DontDownloadKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace DistTestCore
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
|
public class DontDownloadLogsOnFailureAttribute : PropertyAttribute
|
||||||
|
{
|
||||||
|
public const string DontDownloadKey = "DontDownloadLogs";
|
||||||
|
|
||||||
|
public DontDownloadLogsOnFailureAttribute()
|
||||||
|
: base(DontDownloadKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,16 @@
|
||||||
using Core;
|
using Core;
|
||||||
using FileUtils;
|
using FileUtils;
|
||||||
|
using KubernetesWorkflow;
|
||||||
using Logging;
|
using Logging;
|
||||||
using Utils;
|
using Utils;
|
||||||
|
|
||||||
namespace DistTestCore
|
namespace DistTestCore
|
||||||
{
|
{
|
||||||
public class TestLifecycle
|
public class TestLifecycle : IK8sHooks
|
||||||
{
|
{
|
||||||
private readonly DateTime testStart;
|
private readonly DateTime testStart;
|
||||||
private readonly EntryPoint entryPoint;
|
private readonly EntryPoint entryPoint;
|
||||||
|
private readonly List<RunningContainers> runningContainers = new List<RunningContainers>();
|
||||||
|
|
||||||
public TestLifecycle(TestLog log, Configuration configuration, ITimeSet timeSet, string testNamespace)
|
public TestLifecycle(TestLog log, Configuration configuration, ITimeSet timeSet, string testNamespace)
|
||||||
{
|
{
|
||||||
|
@ -17,7 +19,7 @@ namespace DistTestCore
|
||||||
TimeSet = timeSet;
|
TimeSet = timeSet;
|
||||||
testStart = DateTime.UtcNow;
|
testStart = DateTime.UtcNow;
|
||||||
|
|
||||||
entryPoint = new EntryPoint(log, configuration.GetK8sConfiguration(timeSet, testNamespace), configuration.GetFileManagerFolder(), timeSet);
|
entryPoint = new EntryPoint(log, configuration.GetK8sConfiguration(timeSet, this, testNamespace), configuration.GetFileManagerFolder(), timeSet);
|
||||||
CoreInterface = entryPoint.CreateInterface();
|
CoreInterface = entryPoint.CreateInterface();
|
||||||
|
|
||||||
log.WriteLogTag();
|
log.WriteLogTag();
|
||||||
|
@ -51,6 +53,36 @@ namespace DistTestCore
|
||||||
return Time.FormatDuration(testDuration);
|
return Time.FormatDuration(testDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnContainersStarted(RunningContainers rc)
|
||||||
|
{
|
||||||
|
runningContainers.Add(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnContainersStopped(RunningContainers rc)
|
||||||
|
{
|
||||||
|
runningContainers.Remove(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DownloadAllLogs()
|
||||||
|
{
|
||||||
|
var workflow = entryPoint.Tools.CreateWorkflow();
|
||||||
|
foreach (var rc in runningContainers)
|
||||||
|
{
|
||||||
|
foreach (var c in rc.Containers)
|
||||||
|
{
|
||||||
|
DownloadContainerLog(workflow, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DownloadContainerLog(IStartupWorkflow workflow, RunningContainer c)
|
||||||
|
{
|
||||||
|
var file = Log.CreateSubfile();
|
||||||
|
Log.Log($"Downloading container log for '{c.Name}' to file '{file.FullFilename}'...");
|
||||||
|
var handler = new LogDownloadHandler(c.Name, file);
|
||||||
|
workflow.DownloadContainerLog(c, handler);
|
||||||
|
}
|
||||||
|
|
||||||
//public ApplicationIds GetApplicationIds()
|
//public ApplicationIds GetApplicationIds()
|
||||||
//{
|
//{
|
||||||
// //return new ApplicationIds(
|
// //return new ApplicationIds(
|
||||||
|
|
|
@ -15,5 +15,6 @@
|
||||||
public TimeSpan RetryDelay { get; }
|
public TimeSpan RetryDelay { get; }
|
||||||
public string KubernetesNamespace { get; }
|
public string KubernetesNamespace { get; }
|
||||||
public bool AllowNamespaceOverride { get; set; } = true;
|
public bool AllowNamespaceOverride { get; set; } = true;
|
||||||
|
public IK8sHooks Hooks { get; set; } = new DoNothingK8sHooks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
namespace KubernetesWorkflow
|
||||||
|
{
|
||||||
|
public interface IK8sHooks
|
||||||
|
{
|
||||||
|
void OnContainersStarted(RunningContainers runningContainers);
|
||||||
|
void OnContainersStopped(RunningContainers runningContainers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DoNothingK8sHooks : IK8sHooks
|
||||||
|
{
|
||||||
|
public void OnContainersStarted(RunningContainers runningContainers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnContainersStopped(RunningContainers runningContainers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,9 @@ namespace KubernetesWorkflow
|
||||||
|
|
||||||
if (startupConfig.CreateCrashWatcher) CreateCrashWatchers(controller, containers);
|
if (startupConfig.CreateCrashWatcher) CreateCrashWatchers(controller, containers);
|
||||||
|
|
||||||
return new RunningContainers(startupConfig, runningPod, containers);
|
var rc = new RunningContainers(startupConfig, runningPod, containers);
|
||||||
|
cluster.Configuration.Hooks.OnContainersStarted(rc);
|
||||||
|
return rc;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +52,7 @@ namespace KubernetesWorkflow
|
||||||
K8s(controller =>
|
K8s(controller =>
|
||||||
{
|
{
|
||||||
controller.Stop(runningContainers.RunningPod);
|
controller.Stop(runningContainers.RunningPod);
|
||||||
|
cluster.Configuration.Hooks.OnContainersStopped(runningContainers);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue