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
|
||||
}
|
||||
|
||||
public RunawayProcessKillerExtension(String pidfile)
|
||||
public RunawayProcessKillerExtension(String pidfile, int stopTimeoutMs = 5000, bool stopParentFirst = false)
|
||||
{
|
||||
this.Pidfile = pidfile;
|
||||
this.StopTimeout = TimeSpan.FromMilliseconds(5000);
|
||||
this.StopParentProcessFirst = stopParentFirst;
|
||||
}
|
||||
|
||||
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||
|
@ -89,6 +91,7 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
}
|
||||
|
||||
// Now check the process
|
||||
Logger.DebugFormat("Checking the potentially runaway process with PID={0}", pid);
|
||||
Process proc;
|
||||
try
|
||||
{
|
||||
|
@ -113,11 +116,12 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
else
|
||||
{
|
||||
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) {
|
||||
foreach (string key in previousProcessEnvVars.Keys) {
|
||||
Logger.Debug("Env var of " + pid + ": " + key + "=" + previousProcessEnvVars[key]);
|
||||
}
|
||||
//TODO replace by String.Join() in .NET 4
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,12 @@ using NUnit.Framework;
|
|||
using winsw.Extensions;
|
||||
using winsw.Plugins.SharedDirectoryMapper;
|
||||
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
|
||||
{
|
||||
|
@ -58,5 +64,60 @@ namespace winswTests.Extensions
|
|||
manager.FireOnWrapperStarted();
|
||||
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.Text;
|
||||
using winsw;
|
||||
using winsw.Plugins.RunawayProcessKiller;
|
||||
using winswTests.Extensions;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
|
@ -16,6 +18,7 @@ namespace winswTests.Util
|
|||
public string Executable { get; set; }
|
||||
public bool PrintXMLVersion { get; set; }
|
||||
public string XMLComment { get; set; }
|
||||
public List<string> ExtensionXmls { get; private set; }
|
||||
|
||||
private List<String> configEntries;
|
||||
|
||||
|
@ -23,6 +26,7 @@ namespace winswTests.Util
|
|||
private ConfigXmlBuilder()
|
||||
{
|
||||
configEntries = new List<string>();
|
||||
ExtensionXmls = new List<string>();
|
||||
}
|
||||
|
||||
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
|
||||
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");
|
||||
string res = str.ToString();
|
||||
if (dumpConfig)
|
||||
|
@ -88,5 +104,19 @@ namespace winswTests.Util
|
|||
{
|
||||
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.IO;
|
||||
using System.Text;
|
||||
|
@ -29,7 +30,15 @@ namespace winswTests.Util
|
|||
Dictionary<string, string> res = new Dictionary<string, string>();
|
||||
var lines = File.ReadAllLines(filePath);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -36,9 +36,12 @@ namespace winswTests.Util
|
|||
|
||||
// Check several veriables, which are expected to be in Uppercase
|
||||
var envVars = FilesystemTestHelper.parseSetOutput(envFile);
|
||||
Assert.That(envVars.ContainsKey("PROCESSOR_ARCHITECTURE"), "No PROCESSOR_ARCHITECTURE in the injected vars");
|
||||
Assert.That(envVars.ContainsKey("COMPUTERNAME"), "No COMPUTERNAME in the injected vars");
|
||||
Assert.That(envVars.ContainsKey("PATHEXT"), "No PATHEXT in the injected vars");
|
||||
String[] keys = new String[envVars.Count];
|
||||
envVars.Keys.CopyTo(keys, 0);
|
||||
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
|
||||
Assert.That(!envVars.ContainsKey("computername"), "Test error: the environment parsing logic is case-insensitive");
|
||||
|
|
Loading…
Reference in New Issue