mirror of https://github.com/winsw/winsw
[JENKINS-42744] - Improve the ProcessHelperTes, add RunawayProcessKillerTest for the affected logic
parent
9fc518a3d0
commit
bca9bafc66
|
@ -38,9 +38,11 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
// Default initializer
|
// Default initializer
|
||||||
}
|
}
|
||||||
|
|
||||||
public RunawayProcessKillerExtension(String pidfile)
|
public RunawayProcessKillerExtension(String pidfile, int stopTimeoutMs = 5000, bool stopParentFirst = false)
|
||||||
{
|
{
|
||||||
this.Pidfile = pidfile;
|
this.Pidfile = pidfile;
|
||||||
|
this.StopTimeout = TimeSpan.FromMilliseconds(5000);
|
||||||
|
this.StopParentProcessFirst = stopParentFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||||
|
@ -89,6 +91,7 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now check the process
|
// Now check the process
|
||||||
|
Logger.DebugFormat("Checking the potentially runaway process with PID={0}", pid);
|
||||||
Process proc;
|
Process proc;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -113,11 +116,12 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Warn("The process " + pid + " has no " + expectedEnvVarName + " environment variable defined. "
|
Logger.Warn("The process " + pid + " has no " + expectedEnvVarName + " environment variable defined. "
|
||||||
+ "The process has not been started by this service, hence it won't be terminated.");
|
+ "The process has not been started by WinSW, hence it won't be terminated.");
|
||||||
if (Logger.IsDebugEnabled) {
|
if (Logger.IsDebugEnabled) {
|
||||||
foreach (string key in previousProcessEnvVars.Keys) {
|
//TODO replace by String.Join() in .NET 4
|
||||||
Logger.Debug("Env var of " + pid + ": " + key + "=" + previousProcessEnvVars[key]);
|
String[] keys = new String[previousProcessEnvVars.Count];
|
||||||
}
|
previousProcessEnvVars.Keys.CopyTo(keys, 0);
|
||||||
|
Logger.DebugFormat("Env vars of the process with PID={0}: {1}", new Object[] {pid, String.Join(",", keys)});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,12 @@ using NUnit.Framework;
|
||||||
using winsw.Extensions;
|
using winsw.Extensions;
|
||||||
using winsw.Plugins.SharedDirectoryMapper;
|
using winsw.Plugins.SharedDirectoryMapper;
|
||||||
using winsw.Plugins.RunawayProcessKiller;
|
using winsw.Plugins.RunawayProcessKiller;
|
||||||
|
using winswTests.Util;
|
||||||
|
using System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using winsw.Util;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace winswTests.Extensions
|
namespace winswTests.Extensions
|
||||||
{
|
{
|
||||||
|
@ -58,5 +64,60 @@ namespace winswTests.Extensions
|
||||||
manager.FireOnWrapperStarted();
|
manager.FireOnWrapperStarted();
|
||||||
manager.FireBeforeWrapperStopped();
|
manager.FireBeforeWrapperStopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldKillTheSpawnedProcess()
|
||||||
|
{
|
||||||
|
var winswId = "myAppWithRunaway";
|
||||||
|
var extensionId = "runawayProcessKiller";
|
||||||
|
var tmpDir = FilesystemTestHelper.CreateTmpDirectory();
|
||||||
|
|
||||||
|
// Prepare the env var
|
||||||
|
String varName = WinSWSystem.ENVVAR_NAME_SERVICE_ID;
|
||||||
|
var env = new Dictionary<string, string>();
|
||||||
|
env.Add("varName", winswId);
|
||||||
|
|
||||||
|
// Spawn the test process
|
||||||
|
var scriptFile = Path.Combine(tmpDir, "dosleep.bat");
|
||||||
|
var envFile = Path.Combine(tmpDir, "env.txt");
|
||||||
|
File.WriteAllText(scriptFile, "set > " + envFile + "\nsleep 100500");
|
||||||
|
Process proc = new Process();
|
||||||
|
var ps = proc.StartInfo;
|
||||||
|
ps.FileName = scriptFile;
|
||||||
|
ProcessHelper.StartProcessAndCallbackForExit(proc, envVars: env);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Generate extension and ensure that the roundtrip is correct
|
||||||
|
var pidfile = Path.Combine(tmpDir, "process.pid");
|
||||||
|
var sd = ConfigXmlBuilder.create(id: winswId).WithRunawayProcessKiller(new RunawayProcessKillerExtension(pidfile), extensionId).ToServiceDescriptor();
|
||||||
|
WinSWExtensionManager manager = new WinSWExtensionManager(sd);
|
||||||
|
manager.LoadExtensions();
|
||||||
|
var extension = manager.Extensions[extensionId] as RunawayProcessKillerExtension;
|
||||||
|
Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded");
|
||||||
|
Assert.AreEqual(pidfile, extension.Pidfile, "PidFile should have been retained during the config roundtrip");
|
||||||
|
|
||||||
|
// Inject PID
|
||||||
|
File.WriteAllText(pidfile, proc.Id.ToString());
|
||||||
|
|
||||||
|
// Try to terminate
|
||||||
|
Assert.That(!proc.HasExited, "Process " + proc + " has exited before the RunawayProcessKiller extension invocation");
|
||||||
|
extension.OnWrapperStarted();
|
||||||
|
Assert.That(proc.HasExited, "Process " + proc + " should have been terminated by RunawayProcessKiller");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!proc.HasExited)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Test: Killing runaway process with ID=" + proc.Id);
|
||||||
|
ProcessHelper.StopProcessAndChildren(proc.Id, TimeSpan.FromMilliseconds(100), false);
|
||||||
|
if (!proc.HasExited)
|
||||||
|
{
|
||||||
|
// The test is failed here anyway, but we add additional diagnostics info
|
||||||
|
Console.Error.WriteLine("Test: ProcessHelper failed to properly terminate process with ID=" + proc.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using winsw;
|
using winsw;
|
||||||
|
using winsw.Plugins.RunawayProcessKiller;
|
||||||
|
using winswTests.Extensions;
|
||||||
|
|
||||||
namespace winswTests.Util
|
namespace winswTests.Util
|
||||||
{
|
{
|
||||||
|
@ -16,6 +18,7 @@ namespace winswTests.Util
|
||||||
public string Executable { get; set; }
|
public string Executable { get; set; }
|
||||||
public bool PrintXMLVersion { get; set; }
|
public bool PrintXMLVersion { get; set; }
|
||||||
public string XMLComment { get; set; }
|
public string XMLComment { get; set; }
|
||||||
|
public List<string> ExtensionXmls { get; private set; }
|
||||||
|
|
||||||
private List<String> configEntries;
|
private List<String> configEntries;
|
||||||
|
|
||||||
|
@ -23,6 +26,7 @@ namespace winswTests.Util
|
||||||
private ConfigXmlBuilder()
|
private ConfigXmlBuilder()
|
||||||
{
|
{
|
||||||
configEntries = new List<string>();
|
configEntries = new List<string>();
|
||||||
|
ExtensionXmls = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigXmlBuilder create(string id = null, string name = null,
|
public static ConfigXmlBuilder create(string id = null, string name = null,
|
||||||
|
@ -63,6 +67,18 @@ namespace winswTests.Util
|
||||||
// We do not care much about pretty formatting here
|
// We do not care much about pretty formatting here
|
||||||
str.AppendFormat(" {0}\n", entry);
|
str.AppendFormat(" {0}\n", entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extensions
|
||||||
|
if (ExtensionXmls.Count > 0)
|
||||||
|
{
|
||||||
|
str.Append(" <extensions>\n");
|
||||||
|
foreach (string xml in ExtensionXmls)
|
||||||
|
{
|
||||||
|
str.Append(xml);
|
||||||
|
}
|
||||||
|
str.Append(" </extensions>\n");
|
||||||
|
}
|
||||||
|
|
||||||
str.Append("</service>\n");
|
str.Append("</service>\n");
|
||||||
string res = str.ToString();
|
string res = str.ToString();
|
||||||
if (dumpConfig)
|
if (dumpConfig)
|
||||||
|
@ -88,5 +104,19 @@ namespace winswTests.Util
|
||||||
{
|
{
|
||||||
return WithRawEntry(String.Format("<{0}>{1}</{0}>", tagName, value));
|
return WithRawEntry(String.Format("<{0}>{1}</{0}>", tagName, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConfigXmlBuilder WithRunawayProcessKiller(RunawayProcessKillerExtension ext, string extensionId = "killRunawayProcess", bool enabled = true)
|
||||||
|
{
|
||||||
|
var fullyQualifiedExtensionName = ExtensionTestBase.getExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
||||||
|
StringBuilder str = new StringBuilder();
|
||||||
|
str.AppendFormat(" <extension enabled=\"{0}\" className=\"{1}\" id=\"{2}\">\n", new Object[] { enabled, fullyQualifiedExtensionName, extensionId});
|
||||||
|
str.AppendFormat(" <pidfile>{0}</pidfile>\n", ext.Pidfile);
|
||||||
|
str.AppendFormat(" <stopTimeout>{0}</stopTimeout>\n", ext.StopTimeout.TotalMilliseconds);
|
||||||
|
str.AppendFormat(" <stopParentFirst>{0}</stopParentFirst>\n", ext.StopParentProcessFirst);
|
||||||
|
str.Append( " </extension>\n");
|
||||||
|
ExtensionXmls.Add(str.ToString());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -29,7 +30,15 @@ namespace winswTests.Util
|
||||||
Dictionary<string, string> res = new Dictionary<string, string>();
|
Dictionary<string, string> res = new Dictionary<string, string>();
|
||||||
var lines = File.ReadAllLines(filePath);
|
var lines = File.ReadAllLines(filePath);
|
||||||
foreach(var line in lines) {
|
foreach(var line in lines) {
|
||||||
line.Split("=".ToCharArray(), 2);
|
var parsed = line.Split("=".ToCharArray(), 2);
|
||||||
|
if (parsed.Length == 2)
|
||||||
|
{
|
||||||
|
res.Add(parsed[0], parsed[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Fail("Wrong line in the parsed Set output file: " + line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,12 @@ namespace winswTests.Util
|
||||||
|
|
||||||
// Check several veriables, which are expected to be in Uppercase
|
// Check several veriables, which are expected to be in Uppercase
|
||||||
var envVars = FilesystemTestHelper.parseSetOutput(envFile);
|
var envVars = FilesystemTestHelper.parseSetOutput(envFile);
|
||||||
Assert.That(envVars.ContainsKey("PROCESSOR_ARCHITECTURE"), "No PROCESSOR_ARCHITECTURE in the injected vars");
|
String[] keys = new String[envVars.Count];
|
||||||
Assert.That(envVars.ContainsKey("COMPUTERNAME"), "No COMPUTERNAME in the injected vars");
|
envVars.Keys.CopyTo(keys, 0);
|
||||||
Assert.That(envVars.ContainsKey("PATHEXT"), "No PATHEXT in the injected vars");
|
String availableVars = "[" + String.Join(",", keys) + "]";
|
||||||
|
Assert.That(envVars.ContainsKey("PROCESSOR_ARCHITECTURE"), "No PROCESSOR_ARCHITECTURE in the injected vars: " + availableVars);
|
||||||
|
Assert.That(envVars.ContainsKey("COMPUTERNAME"), "No COMPUTERNAME in the injected vars: " + availableVars);
|
||||||
|
Assert.That(envVars.ContainsKey("PATHEXT"), "No PATHEXT in the injected vars: " + availableVars);
|
||||||
|
|
||||||
// And just ensure that the parsing logic is case-sensitive
|
// And just ensure that the parsing logic is case-sensitive
|
||||||
Assert.That(!envVars.ContainsKey("computername"), "Test error: the environment parsing logic is case-insensitive");
|
Assert.That(!envVars.ContainsKey("computername"), "Test error: the environment parsing logic is case-insensitive");
|
||||||
|
|
Loading…
Reference in New Issue