Hello again,
I did manage to apply those two commits manually:
https://github.com/chef/chef/commit/d99e497874e7d08f017376717aa38a4c8d7fecd5
https://github.com/chef/chef/commit/6c10604f1e8e9b557b69449b484df3aae47ee468
omitting a few lines related to some intermediate versions.
Forking is working correctly, the result is visualized in attached munin
grapph. I also attach the diff result.

Such modified client has been used on about 80 servers for over 2
months. The only problem I noticed is that if client run fails, the
stacktrace.out file only contains stacktrace from the parent process.
Child stacktrace is printed out to stderr (it's not included in logs,
visible only while executing the manual, single run). I've made some
custom modifications comparing files with chef 11.12, but I think the
result is not yet what we really expect. If you want to look at it,
please let me know.

If you decide to backport the package, I would suggest to add one small
feature (from newer versions) by chance - I really miss the file_edited?
method in Chef::Util::FileEdit. It's just one getter, the diff is also
attached.

--
Greetings,
Piotr Pańczyk


________________________________

Asseco Business Solutions S.A.
ul. Konrada Wallenroda 4c
20-607 Lublin
tel.: +48 81 535 30 00
fax: +48 81 535 30 05

Sąd Rejonowy Lublin-Wschód w Lublinie z siedzibą w Świdniku
VI Wydział Gospodarczy Krajowego Rejestru Sądowego
KRS 0000028257
NIP 522-26-12-717
kapitał zakładowy 167 090 965,00 zł (w całości opłacony)
www.assecobs.pl

________________________________

Powyższa korespondencja przeznaczona jest wyłącznie dla osoby lub podmiotu, do 
którego jest adresowana i może zawierać informacje o charakterze poufnym lub 
zastrzeżonym. Nieuprawnione wykorzystanie informacji zawartych w wiadomości 
e-mail przez osobę lub podmiot nie będący jej adresatem jest zabronione 
odpowiednimi przepisami prawa. Odbiorca korespondencji, który otrzymał ją 
omyłkowo, proszony jest o niezwłoczne zawiadomienie nadawcy drogą elektroniczną 
lub telefonicznie i usunięcie tej treści z poczty elektronicznej. Dziękujemy. 
Asseco Business Solutions S.A.

________________________________

Weź pod uwagę ochronę środowiska, zanim wydrukujesz ten e-mail.

________________________________

This information is intended only for the person or entity to which it is 
addressed and may contain confidential and/or privileged material. Unauthorized 
use of this information by person or entity other than the intended recipient 
is prohibited by law. If you received this by mistake, please immediately 
contact the sender by e-mail or by telephone and delete this information from 
any computer. Thank you. Asseco Business Solutions S.A.

________________________________

Please consider your environmental responsibility before printing this e-mail.
--- chef-10.12.0.orig/lib/chef/config.rb
+++ chef-10.12.0/lib/chef/config.rb
@@ -184,6 +184,7 @@ class Chef
     run_command_stdout_timeout 120
     solo  false
     splay nil
+    client_fork true
 
     # Set these to enable SSL authentication / mutual-authentication
     # with the server
--- chef-10.12.0.orig/lib/chef/daemon.rb
+++ chef-10.12.0/lib/chef/daemon.rb
@@ -24,6 +24,7 @@ class Chef
   class Daemon
     class << self
       attr_accessor :name
+      attr_accessor :forked_child
 
       # Daemonize the current process, managing pidfiles and process uid/gid
       #
@@ -46,7 +47,7 @@ class Chef
             $stdout.reopen("/dev/null", "a")
             $stderr.reopen($stdout)
             save_pid_file
-            at_exit { remove_pid_file }
+            at_exit { remove_pid_file unless forked_child }
           rescue NotImplementedError => e
             Chef::Application.fatal!("There is no fork: #{e.message}")
           end
--- chef-10.12.0.orig/lib/chef/client.rb
+++ chef-10.12.0/lib/chef/client.rb
@@ -135,48 +135,29 @@ class Chef
     end
 
     # Do a full run for this Chef::Client.  Calls:
+    # * do_run
     #
-    #  * run_ohai - Collect information about the system
-    #  * build_node - Get the last known state, merge with local changes
-    #  * register - If not in solo mode, make sure the server knows about this client
-    #  * sync_cookbooks - If not in solo mode, populate the local cache with the node's cookbooks
-    #  * converge - Bring this system up to date
-    #
+    # This provides a wrapper around #do_run allowing the 
+    # run to be optionally forked.
     # === Returns
-    # true:: Always returns true.
+    # boolean:: Return value from #do_run. Should always returns true.
     def run
-      run_context = nil
-
-      Chef::Log.info("*** Chef #{Chef::VERSION} ***")
-      enforce_path_sanity
-      run_ohai
-      register unless Chef::Config[:solo]
-      build_node
-
-      begin
-
-        run_status.start_clock
-        Chef::Log.info("Starting Chef Run for #{node.name}")
-        run_started
-
-        run_context = setup_run_context
-        converge(run_context)
-        save_updated_node
-
-        run_status.stop_clock
-        Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds")
-        run_completed_successfully
+      if(Chef::Config[:client_fork] && Process.respond_to?(:fork))
+        Chef::Log.info "Forking chef instance to converge..."
+        pid = fork do
+          Chef::Daemon.forked_child = true
+          Chef::Log.info "Forked instance now converging"
+          do_run
+          exit
+        end
+        Chef::Log.info "Fork successful. Waiting for new chef pid: #{pid}"
+        result = Process.waitpid2(pid)
+        raise "Forked convergence run failed" unless result.last.success?
+        Chef::Log.info "Forked child successfully reaped (pid: #{pid})"
         true
-      rescue Exception => e
-        run_status.stop_clock
-        run_status.exception = e
-        run_failed
-        Chef::Log.debug("Re-raising exception: #{e.class} - #{e.message}\n#{e.backtrace.join("\n  ")}")
-        raise
-      ensure
-        run_status = nil
+      else
+        do_run
       end
-      true
     end
 
 
@@ -333,6 +314,50 @@ class Chef
 
     private
 
+    # Do a full run for this Chef::Client.  Calls:
+    #
+    #  * run_ohai - Collect information about the system
+    #  * build_node - Get the last known state, merge with local changes
+    #  * register - If not in solo mode, make sure the server knows about this client
+    #  * sync_cookbooks - If not in solo mode, populate the local cache with the node's cookbooks
+    #  * converge - Bring this system up to date
+    #
+    # === Returns
+    # true:: Always returns true.
+    def do_run
+      run_context = nil
+
+      Chef::Log.info("*** Chef #{Chef::VERSION} ***")
+      enforce_path_sanity
+      run_ohai
+      register unless Chef::Config[:solo]
+      build_node
+
+      begin
+        run_status.start_clock
+        Chef::Log.info("Starting Chef Run for #{node.name}")
+        run_started
+
+        run_context = setup_run_context
+        converge(run_context)
+        save_updated_node
+
+        run_status.stop_clock
+        Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds")
+        run_completed_successfully
+        true
+      rescue Exception => e
+        run_status.stop_clock
+        run_status.exception = e
+        run_failed
+        Chef::Log.debug("Re-raising exception: #{e.class} - #{e.message}\n#{e.backtrace.join("\n  ")}")
+        raise
+      ensure
+        run_status = nil
+      end
+      true
+    end
+
     # Ensures runlist override contains RunListItem instances
     def runlist_override_sanity_check!
       @override_runlist = @override_runlist.split(',') if @override_runlist.is_a?(String)
--- chef-10.12.0.orig/lib/chef/application/client.rb
+++ chef-10.12.0/lib/chef/application/client.rb
@@ -153,6 +153,12 @@ class Chef::Application::Client < Chef::
       }
     }
 
+  option :client_fork,
+    :short        => "-f",
+    :long         => "--[no-]fork",
+    :description  => "Fork client",
+    :boolean      => true
+
   attr_reader :chef_client_json
 
   def initialize
--- chef-10.12.0.orig/lib/chef/application/solo.rb
+++ chef-10.12.0/lib/chef/application/solo.rb
@@ -122,6 +122,12 @@ class Chef::Application::Solo < Chef::Ap
       }
     }
 
+  option :client_fork,
+    :short        => "-f",
+    :long         => "--[no-]fork",
+    :description  => "Fork client",
+    :boolean      => true
+
   attr_reader :chef_solo_json
 
   def initialize
--- chef-10.12.0.orig/lib/chef/util/file_edit.rb
+++ chef-10.12.0/lib/chef/util/file_edit.rb
@@ -36,6 +36,11 @@ class Chef
         raise ArgumentError, "File is blank" unless (@contents = File.new(@original_pathname).readlines).length > 0
       end
 
+      # return if file has been edited
+      def file_edited?
+        @file_edited
+      end
+
       #search the file line by line and match each line with the given regex
       #if matched, replace the whole line with newline.
       def search_file_replace_line(regex, newline)

Reply via email to