Browse Source

Merge pull request #53 from oleg-nenashev/PR_47_refactoring

PR #47 - merged version
Oleg Nenashev 11 years ago
parent
commit
b93e08ee8b
7 changed files with 245 additions and 71 deletions
  1. 3 0
      .gitignore
  2. 32 0
      LogAppenders.cs
  3. 43 3
      Main.cs
  4. 6 0
      README.markdown
  5. 17 3
      ServiceDescriptor.cs
  6. 3 5
      SigIntHelper.cs
  7. 141 60
      Tests/winswTests/ServiceDescriptorTests.cs

+ 3 - 0
.gitignore

@@ -1,3 +1,6 @@
 bin
 obj
 *.suo
+/UpgradeLog.htm
+/winsw.csproj.DotSettings.user
+/winsw.csproj.user

+ 32 - 0
LogAppenders.cs

@@ -163,6 +163,22 @@ namespace winsw
         private string pattern;
         private int period;
 
+        public string Pattern
+        {
+            get
+            {
+                return pattern;
+            }
+        }
+
+        public int Period
+        {
+            get
+            {
+                return period;
+            }
+        }
+
         public TimeBasedRollingLogAppender(string logDirectory, string baseName, string pattern, int period)
             : base(logDirectory, baseName)
         {
@@ -246,6 +262,22 @@ namespace winsw
         private int sizeThreshold;
         private int filesToKeep;
 
+        public int SizeTheshold
+        {
+            get
+            {
+                return sizeThreshold;
+            }
+        }
+
+        public int FilesToKeep
+        {
+            get
+            {
+                return filesToKeep;
+            }
+        }
+
         public SizeBasedRollingLogAppender(string logDirectory, string baseName, int sizeThreshold, int filesToKeep)
             : base(logDirectory, baseName)
         {

+ 43 - 3
Main.cs

@@ -318,16 +318,56 @@ namespace winsw
         }
 
         private void StopProcessAndChildren(int pid)
+        {
+            var childPids = GetChildPids(pid);
+
+            if (descriptor.StopParentProcessFirst)
+            {
+                StopProcess(pid);
+                foreach (var childPid in childPids)
+                {
+                    StopProcessAndChildren(childPid);
+                }
+            }
+            else
+            {
+                foreach (var childPid in childPids)
+                {
+                    StopProcessAndChildren(childPid);
+                }
+                StopProcess(pid);
+            }
+        }
+
+        private List<int> GetChildPids(int pid)
         {
             var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);
+            var childPids = new List<int>();
             foreach (var mo in searcher.Get())
             {
-                StopProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
+                var childProcessId = mo["ProcessID"];
+                WriteEvent("Found child process: " + childProcessId + " Name: " + mo["Name"]);
+                childPids.Add(Convert.ToInt32(childProcessId));
             }
+            return childPids;
+        }
 
-            var proc = Process.GetProcessById(pid);
+        private void StopProcess(int pid)
+        {
+            WriteEvent("Stopping process " + pid);
+            Process proc;
+            try
+            {
+                proc = Process.GetProcessById(pid);
+            }
+            catch (ArgumentException)
+            {
+                WriteEvent("Process " + pid + " is already stopped");
+                return;
+            }
+            
             WriteEvent("Send SIGINT " + pid);
-            bool successful = SigIntHelper.SendSIGINTToProcess(proc,descriptor.StopTimeout);
+            bool successful = SigIntHelper.SendSIGINTToProcess(proc, descriptor.StopTimeout);
             if (successful)
             {
                 WriteEvent("SIGINT to" + pid + " successful");

+ 6 - 0
README.markdown

@@ -292,3 +292,9 @@ Possible values are `idle`, `belownormal`, `normal`, `abovenormal`, `high`, `rea
     <priority>idle</priority>
 
 Specifying a priority higher than normal has unintended consequences. See <a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.processpriorityclass(v=vs.110).aspx">MSDN discussion</a> for details. This feature is intended primarily to launch a process in a lower priority so as not to interfere with the computer's interactive usage.
+
+###stopparentprocessfirst
+Optionally specify the order of service shutdown. If true, the parent process is shutdown first. This is useful when the main process is a console, which can respond to Ctrol+C command and will gracefully shutdown child processes
+```
+<stopparentprocessfirst>true</stopparentprocessfirst>
+```

+ 17 - 3
ServiceDescriptor.cs

@@ -28,13 +28,13 @@ namespace winsw
         /// 
         /// This string is "c:\abc\def\ghi" when the configuration XML is "c:\abc\def\ghi.xml"
         /// </summary>
-        public readonly string BasePath;
+        public string BasePath { get; set; }
         /// <summary>
         /// The file name portion of the configuration file.
         /// 
         /// In the above example, this would be "ghi".
         /// </summary>
-        public readonly string BaseName;
+        public string BaseName { get; set; }
 
         public virtual string ExecutablePath
         {
@@ -580,7 +580,21 @@ namespace winsw
         {
             get
             {
-                return SingleTimeSpanElement(dom, "stoptimeout", TimeSpan.FromSeconds(15));
+                return SingleTimeSpanElement(dom.FirstChild, "stoptimeout", TimeSpan.FromSeconds(15));
+            }
+        }
+
+        public bool StopParentProcessFirst
+        {
+            get
+            {
+                var value = SingleElement("stopparentprocessfirst", true);
+                bool result;
+                if (bool.TryParse(value, out result))
+                {
+                    return result;
+                }
+                return false;
             }
         }
 

+ 3 - 5
SigIntHelper.cs

@@ -51,12 +51,10 @@ namespace winsw
 
                 process.WaitForExit((int)shutdownTimeout.TotalMilliseconds);
 
-                return process.HasExited;
-            }
-            else
-            {
-                return false;
+                return process.HasExited;    
             }
+         
+            return false;
         }
     }
 }

+ 141 - 60
Tests/winswTests/ServiceDescriptorTests.cs

@@ -1,63 +1,65 @@
-using NUnit.Framework;
+using NUnit.Framework;
 using winsw;
 using System.Diagnostics;
-using System.Xml;
-
-namespace winswTests
-{
-    [TestFixture]
-    public class ServiceDescriptorTests
-    {
-
-        private ServiceDescriptor extendedServiceDescriptor;
-
-        private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
-        private const string Username = "User";
-        private const string Password = "Password";
-        private const string Domain = "Domain";
-
-        [SetUp]
-        public void SetUp()
-        {
-            const string SeedXml = "<service>"
-                                   + "<id>service.exe</id>"
-                                   + "<name>Service</name>"
-                                   + "<description>The service.</description>"
-                                   + "<executable>node.exe</executable>"
-                                   + "<arguments>My Arguments</arguments>"
-                                   + "<logmode>rotate</logmode>"
-                                   + "<serviceaccount>"
-                                   +   "<domain>" + Domain + "</domain>"
-                                   +   "<user>" + Username + "</user>"
-                                   +   "<password>" + Password + "</password>"
-                                   + "</serviceaccount>"
-                                   + "<workingdirectory>"
-                                   + ExpectedWorkingDirectory
-                                   + "</workingdirectory>"
-                                   + @"<logpath>C:\logs</logpath>"
+using System.Xml;
+
+namespace winswTests
+{
+    using System;
+
+    [TestFixture]
+    public class ServiceDescriptorTests
+    {
+
+        private ServiceDescriptor extendedServiceDescriptor;
+
+        private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
+        private const string Username = "User";
+        private const string Password = "Password";
+        private const string Domain = "Domain";
+
+        [SetUp]
+        public void SetUp()
+        {
+            const string SeedXml = "<service>"
+                                   + "<id>service.exe</id>"
+                                   + "<name>Service</name>"
+                                   + "<description>The service.</description>"
+                                   + "<executable>node.exe</executable>"
+                                   + "<arguments>My Arguments</arguments>"
+                                   + "<logmode>rotate</logmode>"
+                                   + "<serviceaccount>"
+                                   +   "<domain>" + Domain + "</domain>"
+                                   +   "<user>" + Username + "</user>"
+                                   +   "<password>" + Password + "</password>"
+                                   + "</serviceaccount>"
+                                   + "<workingdirectory>"
+                                   + ExpectedWorkingDirectory
+                                   + "</workingdirectory>"
+                                   + @"<logpath>C:\logs</logpath>"
                                    + "</service>";
-            extendedServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
-        }
-
-        [Test]
-        public void VerifyWorkingDirectory()
-        {
-            System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
-            Assert.That(extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
-        }
-
-        [Test]
-        public void VerifyUsername()
-        {
-            System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
-            Assert.That(extendedServiceDescriptor.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
-        }
-
-        [Test]
-        public void VerifyPassword()
-        {
-            System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
-            Assert.That(extendedServiceDescriptor.ServiceAccountPassword, Is.EqualTo(Password));
+            extendedServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
+        }
+
+        [Test]
+        public void VerifyWorkingDirectory()
+        {
+            System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
+            Assert.That(extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
+        }
+
+        [Test]
+        public void VerifyUsername()
+        {
+            System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
+            Assert.That(extendedServiceDescriptor.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
+        }
+
+        [Test]
+        public void VerifyPassword()
+        {
+            System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
+            Assert.That(extendedServiceDescriptor.ServiceAccountPassword, Is.EqualTo(Password));
         }
 
         [Test]
@@ -71,6 +73,85 @@ namespace winswTests
 
             sd = ServiceDescriptor.FromXML("<service><id>test</id></service>");
             Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal));
-        }
-    }
-}
+        }
+
+        [Test]
+        public void StopParentProcessFirstIsFalseByDefault()
+        {
+            Assert.False(extendedServiceDescriptor.StopParentProcessFirst);
+        }
+
+        [Test]
+        public void CanParseStopParentProcessFirst()
+        {
+            const string SeedXml =   "<service>"
+                                   +    "<stopparentprocessfirst>true</stopparentprocessfirst>"
+                                   + "</service>";
+            var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);
+
+            Assert.True(serviceDescriptor.StopParentProcessFirst);
+        }
+
+        [Test]
+        public void CanParseStopTimeout()
+        {
+            const string SeedXml =   "<service>"
+                                   +    "<stoptimeout>60sec</stoptimeout>"
+                                   + "</service>";
+            var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);
+
+            Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(60)));
+        }
+
+        [Test]
+        public void CanParseStopTimeoutFromMinutes()
+        {
+            const string SeedXml =   "<service>"
+                                   +    "<stoptimeout>10min</stoptimeout>"
+                                   + "</service>";
+            var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);
+
+            Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10)));
+        }   
+        
+        [Test]
+        public void LogModeRollBySize()
+        {
+            const string SeedXml =   "<service>"
+                                   + "<logpath>c:\\</logpath>"
+                                   + "<log mode=\"roll-by-size\">"
+                                   +    "<sizeThreshold>112</sizeThreshold>"
+                                   +    "<keepFiles>113</keepFiles>"
+                                   + "</log>"
+                                   + "</service>";
+            
+            var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);
+            serviceDescriptor.BaseName = "service";
+
+            var logHandler = serviceDescriptor.LogHandler as SizeBasedRollingLogAppender;
+            Assert.NotNull(logHandler);
+            Assert.That(logHandler.SizeTheshold, Is.EqualTo(112 * 1024));
+            Assert.That(logHandler.FilesToKeep, Is.EqualTo(113));
+        }
+
+        [Test]
+        public void LogModeRollByTime()
+        {
+            const string SeedXml = "<service>"
+                                   + "<logpath>c:\\</logpath>"
+                                   + "<log mode=\"roll-by-time\">"
+                                   +    "<period>7</period>"
+                                   +    "<pattern>log pattern</pattern>"
+                                   + "</log>"
+                                   + "</service>";
+
+            var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);
+            serviceDescriptor.BaseName = "service";
+
+            var logHandler = serviceDescriptor.LogHandler as TimeBasedRollingLogAppender;
+            Assert.NotNull(logHandler);
+            Assert.That(logHandler.Period, Is.EqualTo(7));
+            Assert.That(logHandler.Pattern, Is.EqualTo("log pattern"));
+        }
+    }
+}