extras-buildsys/builder builder.py,1.41,1.42

Daniel Williams (dcbw) fedora-extras-commits at redhat.com
Tue Sep 13 18:43:03 UTC 2005


Author: dcbw

Update of /cvs/fedora/extras-buildsys/builder
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv15037/builder

Modified Files:
	builder.py 
Log Message:
2005-09-13  Dan Williams  <dcbw at redhat.com>

    * builder/builder.py
        - Try to tighten up child process handling to not
            leave defunct mock processes around




Index: builder.py
===================================================================
RCS file: /cvs/fedora/extras-buildsys/builder/builder.py,v
retrieving revision 1.41
retrieving revision 1.42
diff -u -r1.41 -r1.42
--- builder.py	1 Sep 2005 19:54:49 -0000	1.41
+++ builder.py	13 Sep 2005 18:43:01 -0000	1.42
@@ -82,6 +82,7 @@
         self.buildarch = buildarch
         self._starttime = time.time()
         self._endtime = 0
+        self._mockstarttime = 0
         self._uniqid = uniqid
         self._status = 'init'
         self._die = False
@@ -136,28 +137,34 @@
     def endtime(self):
         return self._endtime
 
-    def die(self, sig=15):
+    def die(self):
         if self.is_done_status() or self._done_status == 'killed':
             return True
         self._die = True
         return True
 
     def _handle_death(self):
-        self._log("Killing build process...\n")
         self._die = False
+        self._done_status = 'killed'
+        self._log("Killing build process...\n")
+
         # Don't try to kill a running cleanup process
         if self._status != 'cleanup' and self._childpid:
             try:
-                os.kill(self._childpid, 15)
+                os.kill(self._childpid, 9)
             except OSError, e:
                 self._log("Couldn't kill process %d: %s\n" % (self._childpid, e))
-
-        self._log("Killed.\n");
-        self._done_status = 'killed'
-
-        # Don't start cleanup over top of an existing cleanup process
-        if self._status != 'cleanup':
+            else:
+                # Ensure child process is reaped
+                self._log("Waiting for mock process %d to exit...\n" % self._childpid)
+                try:
+                    (pid, status) = os.waitpid(self._childpid, 0)
+                except OSError, e:
+                    pass
+                self._log("Mock process %d exited.\n" % self._childpid)
+            self._childpid = 0
             self._start_cleanup()
+        self._log("Killed.\n");
 
     def _log(self, string):
         if string and self._log_fd:
@@ -169,14 +176,6 @@
                 sys.stdout.write(s + string)
                 sys.stdout.flush()
 
-    def _start_srpm_download(self):
-        self._status = 'downloading'
-        self._log("Starting download of %s.\n" % self._srpm_url)
-        target_dir = os.path.dirname(self._srpm_path)
-        dl_thread = FileDownloader.FileDownloader(self.dl_callback, self._srpm_url, self._srpm_url,
-                        target_dir, ['.src.rpm'], certs)
-        dl_thread.start()
-
     def dl_callback(self, dl_status, cb_data):
         url = cb_data
         if dl_status == 'done':
@@ -230,45 +229,9 @@
 
         self._mock_log = os.path.join(self._result_dir, "mock-output.log")
         self._childpid = ExecUtils.exec_with_redirect(cmd, args, None, self._mock_log, self._mock_log)
+        self._mockstarttime = time.time()
         self._status = 'prepping'
 
-        # Poll a bit to wait for mock to write out the status file if
-        # its not there yet.
-        start_time = time.time()
-        mockstatusfile = os.path.join(self._state_dir, 'status')
-        while not os.path.exists(mockstatusfile):
-            try:
-                time.sleep(0.5)
-            except KeyboardInterrupt:
-                pass
-
-            # if mock exited with an error report that error and not
-            # the missing status file.
-            (aux_pid, status) = os.waitpid(self._childpid, os.WNOHANG)
-            status = os.WEXITSTATUS(status)
-            if aux_pid:
-                # If mock exits anywhere here, something is wrong no matter
-                # what it's exit status
-                self._childpid = 0
-                self._copy_mock_output_to_log()
-                self._status = 'failed'
-                break
-
-            # Kill mock after 15s if it didn't dump the status file
-            if time.time() - start_time > 15:
-                self._copy_mock_output_to_log()
-                self._log("Timed out waiting for the mock status file!  %s\n" % mockstatusfile)
-                try:
-                    self._log("Killing mock...\n")
-                    os.kill(self._childpid, 15)
-                except OSError, e:
-                    self._log("Couldn't kill mock process %d: %s\n" % (self._childpid, e))
-                else:
-                    self._log("Killed.\n")
-
-                self._status = 'failed'
-                break
-
     def _start_cleanup(self):
         self._log("Cleaning up the buildroot...\n")
 
@@ -288,7 +251,6 @@
 
         self._log("   %s\n" % string.join(args))
         self._childpid = ExecUtils.exec_with_redirect(cmd, args, None, None, None)
-
         self._status = 'cleanup'
 
     def _mock_is_prepping(self):
@@ -377,8 +339,7 @@
         if self._status == 'done':
             self._log("Job completed successfully.\n")
         elif self._status == 'failed':
-            if self._childpid:
-                self._log("Job failed due to mock errors!  Please see output in root.log and build.log\n")
+            self._log("Job failed due to mock errors!  Please see output in root.log and build.log\n")
         elif self._status == 'killed':
             self._log("Job failed because it was killed.\n")
 
@@ -387,7 +348,12 @@
             self._log_fd = None
 
     def _status_init(self):
-        self._start_srpm_download()
+        self._log("Starting download of %s.\n" % self._srpm_url)
+        self._status = 'downloading'
+        target_dir = os.path.dirname(self._srpm_path)
+        dl_thread = FileDownloader.FileDownloader(self.dl_callback, self._srpm_url, self._srpm_url,
+                        target_dir, ['.src.rpm'], certs)
+        dl_thread.start()
 
     def _status_downloading(self):
         pass
@@ -403,27 +369,44 @@
                 self._log("Waiting for repository to unlock before starting the build...\n")
                 self._repo_locked_msg = True
 
-    def _status_prepping(self):
-        if not self._mock_config and self._mock_is_prepping():
-            self._mock_config = self._read_mock_config()
-        if not self._mock_using_repo():
-            self._status = 'building'
-
-    def _status_building(self):
+    def _watch_mock(self, good_exit, bad_exit):
         (aux_pid, status) = os.waitpid(self._childpid, os.WNOHANG)
         status = os.WEXITSTATUS(status)
         if aux_pid:
             self._childpid = 0
             if status == 0:
-                self._done_status = 'done'
+                self._done_status = good_exit
             elif status > 0:
-                self._done_status = 'failed'
+                self._done_status = bad_exit
 
+            self._copy_mock_output_to_log()
             self._start_cleanup()
 
+    def _status_prepping(self):
+        self._watch_mock('failed', 'failed')
+
+        # We need to make sure that mock has dumped the status file withing a certain
+        # amount of time, otherwise we can't tell what it's doing
+        mockstatusfile = os.path.join(self._state_dir, 'status')
+        if not os.path.exists(mockstatusfile):
+            # something is wrong if mock takes more than 15s to write the status file
+            if time.time() > self._mockstarttime + 15:
+                self._mockstarttime = 0
+                self._log("Timed out waiting for the mock status file!  %s\n" % mockstatusfile)
+                self.die()
+        else:
+            if not self._mock_config and self._mock_is_prepping():
+                self._mock_config = self._read_mock_config()
+            if not self._mock_using_repo():
+                self._status = 'building'
+
+    def _status_building(self):
+        self._watch_mock('done', 'failed')
+
     def _status_cleanup(self):
         (aux_pid, status) = os.waitpid(self._childpid, os.WNOHANG)
         if aux_pid:
+            self._childpid = 0
             # Mock exited
             self._status = self._done_status
             if self._mock_config:
@@ -461,6 +444,8 @@
             time.sleep(3)
 
         self._endtime = time.time()
+        if self._childpid:
+            self._log("ERROR: childpid was !NULL  (%d)" % self._childpid)
         self._controller.notify_job_done(self)
 
     def _find_files(self):
@@ -790,8 +775,6 @@
 
 
 if __name__ == '__main__':
-#    state={'opts': True, 'host': None, 'archs': [],
-#      'daemon': False, 'pidfile': None, 'logfile': None}
 
     usage = "Usage: %s  [-p <pidfile>] [-l <logfile>] [-d] -c <configfile>" % sys.argv[0]
     parser = OptionParser(usage=usage)




More information about the fedora-extras-commits mailing list