Browse Source

Merge pull request #2407 from TechEmpower/remove-testrunner

Remove testrunner
Mike Smith 8 years ago
parent
commit
280fb7f857

+ 0 - 1
benchmark.cfg.example

@@ -3,7 +3,6 @@
 client_host=127.0.0.1
 client_host=127.0.0.1
 client_identity_file=None
 client_identity_file=None
 client_user=techempower
 client_user=techempower
-runner_user=testrunner
 database_host=127.0.0.1
 database_host=127.0.0.1
 database_identity_file=None
 database_identity_file=None
 database_os=linux
 database_os=linux

+ 2 - 2
config/php-fpm.conf

@@ -139,8 +139,8 @@ events.mechanism = epoll
 ;       will be used.
 ;       will be used.
 ; Note: TFB does not run php-fpm as root, and therefore these directives are 
 ; Note: TFB does not run php-fpm as root, and therefore these directives are 
 ;       ignored. Commenting them out avoids spurious log messages
 ;       ignored. Commenting them out avoids spurious log messages
-; user = testrunner
-; group = testrunner
+; user = 
+; group = 
 
 
 ; The address on which to accept FastCGI requests.
 ; The address on which to accept FastCGI requests.
 ; Valid syntaxes are:
 ; Valid syntaxes are:

+ 0 - 18
config/travis_setup.sh

@@ -39,26 +39,8 @@ echo "database_host=127.0.0.1"                         >> benchmark.cfg
 echo "server_host=127.0.0.1"                           >> benchmark.cfg
 echo "server_host=127.0.0.1"                           >> benchmark.cfg
 echo "client_user=travis"                              >> benchmark.cfg
 echo "client_user=travis"                              >> benchmark.cfg
 echo "database_user=travis"                            >> benchmark.cfg
 echo "database_user=travis"                            >> benchmark.cfg
-echo "runner_user=testrunner"                          >> benchmark.cfg
-
-# Create the new testrunner user
-sudo useradd testrunner
-# Give him a home dir
-sudo mkdir /home/testrunner
-# Make testrunner the owner of his home dir
-sudo chown testrunner:testrunner /home/testrunner
-# Add the testrunner user to every group that the travis user is in
-sudo sed -i 's|:travis|:travis,testrunner,benchmarkdbuser|g' /etc/group
-# Maybe unneeded - add the travis user to the testrunner group
-sudo sed -i 's|testrunner:x:\(.*\):|testrunner:x:\1:travis|g' /etc/group
-# Need to add testrunner to the sudoers group AND default him to a sudoers
-# because the travis user isn't in the sudo group - he's a sudoer.
-echo "testrunner ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
-# Set the default shell for testrunner to /bin/bash
-sudo sed -i 's|/home/testrunner:/bin/sh|/home/testrunner:/bin/bash|g' /etc/passwd
 
 
 mkdir installs
 mkdir installs
-sudo chown testrunner:testrunner installs
 
 
 # =============Setup Databases===========================
 # =============Setup Databases===========================
 # NOTE: Do not run `--install database` in travis-ci! 
 # NOTE: Do not run `--install database` in travis-ci! 

+ 1 - 29
deployment/vagrant-common/bootstrap.sh

@@ -59,7 +59,6 @@ if [ ! -e "~/.firstboot" ]; then
   echo "export TFB_DATABASE_HOST=$DATABA_IP" >> ~/.bash_profile
   echo "export TFB_DATABASE_HOST=$DATABA_IP" >> ~/.bash_profile
   echo "export TFB_CLIENT_USER=$USER" >> ~/.bash_profile
   echo "export TFB_CLIENT_USER=$USER" >> ~/.bash_profile
   echo "export TFB_DATABASE_USER=$USER" >> ~/.bash_profile
   echo "export TFB_DATABASE_USER=$USER" >> ~/.bash_profile
-  echo "export TFB_RUNNER_USER=testrunner" >> ~/.bash_profile
   echo "export FWROOT=$HOME/FrameworkBenchmarks" >> ~/.bash_profile 
   echo "export FWROOT=$HOME/FrameworkBenchmarks" >> ~/.bash_profile 
   source ~/.bash_profile
   source ~/.bash_profile
 
 
@@ -75,12 +74,6 @@ if [ ! -e "~/.firstboot" ]; then
   echo $CLIENT_IP TFB-client   | sudo tee --append /etc/hosts
   echo $CLIENT_IP TFB-client   | sudo tee --append /etc/hosts
   echo $SERVER_IP TFB-server   | sudo tee --append /etc/hosts
   echo $SERVER_IP TFB-server   | sudo tee --append /etc/hosts
 
 
-  # Add user to run tests
-  sudo adduser --disabled-password --gecos "" testrunner
-  # WARN: testrunner will NOT have sudo access by round 11
-  #       please begin migrating scripts to not rely on sudo.
-  sudo bash -c "echo 'testrunner ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-tfb-testrunner"
-
   # Update hostname to reflect our current role
   # Update hostname to reflect our current role
   if [ "$ROLE" != "all" ]; then
   if [ "$ROLE" != "all" ]; then
     echo "Updating hostname"
     echo "Updating hostname"
@@ -111,8 +104,7 @@ if [ ! -e "~/.firstboot" ]; then
 
 
     # vboxfs does not support chown or chmod, which we need. 
     # vboxfs does not support chown or chmod, which we need. 
     # We therefore bind-mount a normal linux directory so we can
     # We therefore bind-mount a normal linux directory so we can
-    # use these operations. This enables us to 
-    # use `chown -R testrunner:testrunner $FWROOT/installs` later
+    # use these operations.
     #echo "Mounting over your installs folder"
     #echo "Mounting over your installs folder"
     #mkdir -p /tmp/TFB_installs
     #mkdir -p /tmp/TFB_installs
     #mkdir -p /FrameworkBenchmarks/installs
     #mkdir -p /FrameworkBenchmarks/installs
@@ -125,26 +117,6 @@ if [ ! -e "~/.firstboot" ]; then
     source ~/FrameworkBenchmarks/toolset/setup/linux/prerequisites.sh
     source ~/FrameworkBenchmarks/toolset/setup/linux/prerequisites.sh
   #fi
   #fi
 
 
-  # Everyone gets SSH access to localhost
-  echo "Setting up SSH access to localhost"
-  ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
-  cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
-  sudo -u testrunner mkdir -p /home/testrunner/.ssh
-  sudo -u testrunner ssh-keygen -t rsa -N '' -f /home/testrunner/.ssh/id_rsa
-  sudo -u testrunner bash -c "cat /home/testrunner/.ssh/id_rsa.pub >> /home/testrunner/.ssh/authorized_keys"
-  sudo -u testrunner bash -c "cat /home/vagrant/.ssh/authorized_keys >> /home/testrunner/.ssh/authorized_keys"
-  chmod 600 ~/.ssh/authorized_keys
-  sudo -u testrunner chmod 600 /home/testrunner/.ssh/authorized_keys
-  
-  export RUNNER=testrunner
-  export ME=$(id -u -n)
-  sudo chown $RUNNER:$RUNNER /home/$RUNNER
-  sudo sed -i 's|:'"$ME"'|:'"$ME"','"$RUNNER"'|g' /etc/group
-  sudo sed -i 's|'"$ME"':x:\(.*\):|'"$ME"':x:\1:'"$RUNNER"'|g' /etc/group
-  sudo sed -i 's|'"$RUNNER"':x:\(.*\):|'"$RUNNER"':x:\1:'"$ME"'|g' /etc/group
-  echo "$RUNNER ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
-  sudo sed -i 's|/home/'"$RUNNER"':.*|/home/'"$RUNNER"':/bin/bash|g' /etc/passwd
-
   # Enable remote SSH access if we are running production environment
   # Enable remote SSH access if we are running production environment
   # Note : this is always copied from the local working copy using a
   # Note : this is always copied from the local working copy using a
   #        file provisioner. While they exist in the git clone we just 
   #        file provisioner. While they exist in the git clone we just 

+ 4 - 6
deployment/vagrant-common/core.rb

@@ -106,12 +106,10 @@ def provider_virtualbox(config, role, ip_address='172.16.0.16')
     # The VirtualBox file system for shared folders (vboxfs)
     # The VirtualBox file system for shared folders (vboxfs)
     # does not support posix's chown/chmod - these can only 
     # does not support posix's chown/chmod - these can only 
     # be set at mount time, and they are uniform for the entire
     # be set at mount time, and they are uniform for the entire
-    # shared directory. We require chown, because we have the 
-    # testrunner user account, so this is a problem. To mitigate
-    # the effects, we set the folders and files to 777 permissions. 
-    # Even though we cannot chown them to testrunner, with 777 and 
-    # owner vagrant *most* of the software works ok. Occasional 
-    # issues are still possible. 
+    # shared directory. To mitigate the effects, we set the 
+    # folders and files to 777 permissions. 
+    # With 777 and wner vagrant *most* of the software works ok. 
+    # Occasional issues are still possible. 
     #
     #
     # See mitchellh/vagrant#4997
     # See mitchellh/vagrant#4997
     # See http://superuser.com/a/640028/136050
     # See http://superuser.com/a/640028/136050

+ 1 - 1
frameworks/CSharp/revenj/benchmark_config.json

@@ -1,5 +1,5 @@
 {
 {
-  "framework": "Revenj",
+  "framework": "revenj",
   "tests": [{
   "tests": [{
     "windows": {
     "windows": {
       "setup_file": "setup",
       "setup_file": "setup",

+ 0 - 1
frameworks/CSharp/revenj/setup.sh

@@ -33,4 +33,3 @@ cat $TROOT/Revenj.Http.exe.config | sed 's|\(ConnectionString.*server=\)localhos
 
 
 echo "Running the Revenj instance"
 echo "Running the Revenj instance"
 mono $TROOT/exe/Revenj.Http.exe
 mono $TROOT/exe/Revenj.Http.exe
-sleep 5

+ 0 - 1
frameworks/Java/beyondj/setup.sh

@@ -14,4 +14,3 @@ cd ../../
 
 
 echo "Launching BeyondJ from location:$PWD"
 echo "Launching BeyondJ from location:$PWD"
 java -jar beyondj-launcher/deploy/beyondj-launcher-1.0-SNAPSHOT.jar system.platform.dbserver=${DBHOST} numInstances=10
 java -jar beyondj-launcher/deploy/beyondj-launcher-1.0-SNAPSHOT.jar system.platform.dbserver=${DBHOST} numInstances=10
-

+ 1 - 1
frameworks/Java/revenj/benchmark_config.json

@@ -1,5 +1,5 @@
 {
 {
-  "framework": "Revenj.JVM",
+  "framework": "revenj",
   "tests": [{
   "tests": [{
     "default": {
     "default": {
       "setup_file": "setup",
       "setup_file": "setup",

+ 0 - 3
frameworks/PHP/README.md

@@ -136,9 +136,6 @@ because some gitignore files in this repo contain `*.lock` to avoid Ruby's lock
 If you are prompted for input during the `run-tests.py` script above, then you
 If you are prompted for input during the `run-tests.py` script above, then you
 need to generate your lock file manually so that you may answer the input 
 need to generate your lock file manually so that you may answer the input 
 queries as they are shown. Use these steps
 queries as they are shown. Use these steps
-
-    # Switch to the user that runs tests
-    sudo su testrunner
     
     
     # Define the environment variables you need (modify as needed)
     # Define the environment variables you need (modify as needed)
     export IROOT=/home/you/FrameworkBenchmarks/installs
     export IROOT=/home/you/FrameworkBenchmarks/installs

+ 50 - 28
toolset/benchmark/benchmarker.py

@@ -546,7 +546,6 @@ class Benchmarker:
           p.communicate("""
           p.communicate("""
             sudo restart mysql
             sudo restart mysql
             sudo restart mongod
             sudo restart mongod
-            sudo service redis-server restart
             sudo service postgresql restart
             sudo service postgresql restart
             sudo service cassandra restart
             sudo service cassandra restart
             /opt/elasticsearch/elasticsearch restart
             /opt/elasticsearch/elasticsearch restart
@@ -556,7 +555,6 @@ class Benchmarker:
           st = verify_database_connections([
           st = verify_database_connections([
             ("mysql", self.database_host, 3306),
             ("mysql", self.database_host, 3306),
             ("mongodb", self.database_host, 27017),
             ("mongodb", self.database_host, 27017),
-            ("redis", self.database_host, 6379),
             ("postgresql", self.database_host, 5432),
             ("postgresql", self.database_host, 5432),
             ("cassandra", self.database_host, 9160),
             ("cassandra", self.database_host, 9160),
             ("elasticsearch", self.database_host, 9200)
             ("elasticsearch", self.database_host, 9200)
@@ -566,21 +564,16 @@ class Benchmarker:
         self.__cleanup_leftover_processes_before_test();
         self.__cleanup_leftover_processes_before_test();
 
 
         if self.__is_port_bound(test.port):
         if self.__is_port_bound(test.port):
-          # This can happen sometimes - let's try again
-          self.__stop_test(out)
+          # We gave it our all
+          self.__write_intermediate_results(test.name, "port " + str(test.port) + " is not available before start")
+          out.write(header("Error: Port %s is not available, cannot start %s" % (test.port, test.name)))
           out.flush()
           out.flush()
-          time.sleep(15)
-          if self.__is_port_bound(test.port):
-            # We gave it our all
-            self.__write_intermediate_results(test.name, "port " + str(test.port) + " is not available before start")
-            out.write(header("Error: Port %s is not available, cannot start %s" % (test.port, test.name)))
-            out.flush()
-            print "Error: Unable to recover port, cannot start test"
-            return exit_with_code(1)
+          print "Error: Unable to recover port, cannot start test"
+          return exit_with_code(1)
 
 
-        result = test.start(out)
+        result, process = test.start(out)
         if result != 0:
         if result != 0:
-          self.__stop_test(out)
+          self.__stop_test(out, process)
           time.sleep(5)
           time.sleep(5)
           out.write( "ERROR: Problem starting {name}\n".format(name=test.name) )
           out.write( "ERROR: Problem starting {name}\n".format(name=test.name) )
           out.flush()
           out.flush()
@@ -618,15 +611,15 @@ class Benchmarker:
         ##########################
         ##########################
         out.write(header("Stopping %s" % test.name))
         out.write(header("Stopping %s" % test.name))
         out.flush()
         out.flush()
-        self.__stop_test(out)
+        self.__stop_test(out, process)
         out.flush()
         out.flush()
-        time.sleep(15)
+        time.sleep(5)
 
 
         if self.__is_port_bound(test.port):
         if self.__is_port_bound(test.port):
           # This can happen sometimes - let's try again
           # This can happen sometimes - let's try again
-          self.__stop_test(out)
+          self.__stop_test(out, process)
           out.flush()
           out.flush()
-          time.sleep(15)
+          time.sleep(5)
           if self.__is_port_bound(test.port):
           if self.__is_port_bound(test.port):
             # We gave it our all
             # We gave it our all
             self.__write_intermediate_results(test.name, "port " + str(test.port) + " was not released by stop")
             self.__write_intermediate_results(test.name, "port " + str(test.port) + " was not released by stop")
@@ -636,7 +629,6 @@ class Benchmarker:
 
 
         out.write(header("Stopped %s" % test.name))
         out.write(header("Stopped %s" % test.name))
         out.flush()
         out.flush()
-        time.sleep(5)
 
 
         ##########################################################
         ##########################################################
         # Remove contents of  /tmp folder
         # Remove contents of  /tmp folder
@@ -669,7 +661,7 @@ class Benchmarker:
         traceback.print_exc(file=out)
         traceback.print_exc(file=out)
         out.flush()
         out.flush()
         try:
         try:
-          self.__stop_test(out)
+          self.__stop_test(out, process)
         except (subprocess.CalledProcessError) as e:
         except (subprocess.CalledProcessError) as e:
           self.__write_intermediate_results(test.name,"<setup.py>#stop() raised an error")
           self.__write_intermediate_results(test.name,"<setup.py>#stop() raised an error")
           out.write(header("Subprocess Error: Test .stop() raised exception %s" % test.name))
           out.write(header("Subprocess Error: Test .stop() raised exception %s" % test.name))
@@ -680,7 +672,7 @@ class Benchmarker:
       # TODO - subprocess should not catch this exception!
       # TODO - subprocess should not catch this exception!
       # Parent process should catch it and cleanup/exit
       # Parent process should catch it and cleanup/exit
       except (KeyboardInterrupt) as e:
       except (KeyboardInterrupt) as e:
-        self.__stop_test(out)
+        self.__stop_test(out, process)
         out.write(header("Cleaning up..."))
         out.write(header("Cleaning up..."))
         out.flush()
         out.flush()
         self.__finish()
         self.__finish()
@@ -697,16 +689,46 @@ class Benchmarker:
   # __stop_test(benchmarker)
   # __stop_test(benchmarker)
   # Stops all running tests
   # Stops all running tests
   ############################################################
   ############################################################
-  def __stop_test(self, out):
+  def __stop_test(self, out, process):
+    if process is not None and process.poll() is None:
+      # Stop 
+      pids = self.__find_child_processes(process.pid)
+      if pids is not None:
+        stop = ['kill', '-STOP'] + pids
+        subprocess.call(stop, stderr=out, stdout=out)
+      pids = self.__find_child_processes(process.pid)
+      if pids is not None:
+        term = ['kill', '-TERM'] + pids
+        subprocess.call(term, stderr=out, stdout=out)
+      # Okay, if there are any more PIDs, kill them harder
+      pids = self.__find_child_processes(process.pid)
+      if pids is not None:
+        kill = ['kill', '-KILL'] + pids
+        subprocess.call(kill, stderr=out, stdout=out)
+      process.terminate()
+  ############################################################
+  # End __stop_test
+  ############################################################
+
+  ############################################################
+  # __find_child_processes
+  # Recursively finds all child processes for the given PID.
+  ############################################################
+  def __find_child_processes(self, pid):
+    toRet = []
     try:
     try:
-      subprocess.check_call('sudo killall -s 9 -u %s' % self.runner_user, shell=True, stderr=out, stdout=out)
-      retcode = 0
-    except Exception:
-      retcode = 1
+      pids = subprocess.check_output(['pgrep','-P',str(pid)]).split()
+      toRet.extend(pids)
+      for aPid in pids:
+        toRet.extend(self.__find_child_processes(aPid))
+    except:
+      # pgrep will return a non-zero status code if there are no
+      # processes who have a PPID of PID.
+      pass
 
 
-    return retcode
+    return toRet
   ############################################################
   ############################################################
-  # End __stop_test
+  # End __find_child_processes
   ############################################################
   ############################################################
 
 
   def is_port_bound(self, port):
   def is_port_bound(self, port):

+ 17 - 34
toolset/benchmark/framework_test.py

@@ -187,12 +187,16 @@ class FrameworkTest:
                 self.benchmarker.threads,
                 self.benchmarker.threads,
                 max(self.benchmarker.concurrency_levels)))
                 max(self.benchmarker.concurrency_levels)))
 
 
-    # Always ensure that IROOT belongs to the runner_user
+    # Always ensure that IROOT exists
     if not os.path.exists(self.install_root):
     if not os.path.exists(self.install_root):
       os.mkdir(self.install_root)
       os.mkdir(self.install_root)
-    chown = "sudo chown -R %s:%s %s" % (self.benchmarker.runner_user,
-      self.benchmarker.runner_user, os.path.join(self.fwroot, self.install_root))
-    subprocess.check_call(chown, shell=True, cwd=self.fwroot, executable='/bin/bash')
+
+    if not os.path.exists(os.path.join(self.install_root,"TFBReaper")):
+      subprocess.check_call(['gcc', 
+        '-std=c99', 
+        '-o%s/TFBReaper' % self.install_root, 
+        os.path.join(self.fwroot,'toolset/setup/linux/TFBReaper.c')  ],
+        stderr=out, stdout=out)
 
 
     # Run the module start inside parent of TROOT
     # Run the module start inside parent of TROOT
     #  - we use the parent as a historical accident, a number of tests
     #  - we use the parent as a historical accident, a number of tests
@@ -201,31 +205,7 @@ class FrameworkTest:
     os.chdir(os.path.dirname(self.troot))
     os.chdir(os.path.dirname(self.troot))
     logging.info("Running setup module start (cwd=%s)", self.directory)
     logging.info("Running setup module start (cwd=%s)", self.directory)
 
 
-    # Run the start script for the test as the "testrunner" user
-    #
-    # `sudo` - Switching user requires superuser privs
-    #   -u [username] The username
-    #   -E Preserves the current environment variables
-    #   -H Forces the home var (~) to be reset to the user specified
-    # `stdbuf` - Disable buffering, send output to python ASAP
-    #   -o0 zero-sized buffer for stdout
-    #   -e0 zero-sized buffer for stderr
-    # `bash` - Run the setup.sh script using bash
-    #   -e Force bash to exit on first error
-    #   -x Turn on bash tracing e.g. print commands before running
-    #
-    # Most servers do not output to stdout/stderr while serving
-    # requests so there is no performance hit from disabling
-    # output buffering. This disabling is necessary to
-    # a) allow TFB to show output in real time and b) avoid loosing
-    # output in the buffer when the testrunner processes are forcibly
-    # killed
-    #
-    # See http://www.pixelbeat.org/programming/stdio_buffering/
-    # See https://blogs.gnome.org/markmc/2013/06/04/async-io-and-python/
-    # See http://eyalarubas.com/python-subproc-nonblock.html
-    command = 'sudo -u %s -E -H stdbuf -o0 -e0 bash -exc "source %s && source %s.sh"' % (
-      self.benchmarker.runner_user,
+    command = 'bash -exc "source %s && source %s.sh"' % (
       bash_functions_path,
       bash_functions_path,
       os.path.join(self.troot, self.setup_file))
       os.path.join(self.troot, self.setup_file))
 
 
@@ -238,7 +218,7 @@ class FrameworkTest:
       export MAX_THREADS=%s     &&  \\
       export MAX_THREADS=%s     &&  \\
       export MAX_CONCURRENCY=%s && \\
       export MAX_CONCURRENCY=%s && \\
       cd %s && \\
       cd %s && \\
-      %s''' % (self.fwroot,
+      %s/TFBReaper "bash -exc \\\"source %s && source %s.sh\\\"''' % (self.fwroot,
         self.directory,
         self.directory,
         self.install_root,
         self.install_root,
         self.database_host,
         self.database_host,
@@ -246,7 +226,9 @@ class FrameworkTest:
         self.benchmarker.threads,
         self.benchmarker.threads,
         max(self.benchmarker.concurrency_levels),
         max(self.benchmarker.concurrency_levels),
         self.directory,
         self.directory,
-        command)
+        self.install_root,
+        bash_functions_path,
+        os.path.join(self.troot, self.setup_file))
     logging.info("To run %s manually, copy/paste this:\n%s", self.name, debug_command)
     logging.info("To run %s manually, copy/paste this:\n%s", self.name, debug_command)
 
 
 
 
@@ -265,8 +247,9 @@ class FrameworkTest:
       out.flush()
       out.flush()
 
 
     # Start the setup.sh command
     # Start the setup.sh command
-    p = subprocess.Popen(command, cwd=self.directory,
-          shell=True, stdout=subprocess.PIPE,
+    p = subprocess.Popen(["%s/TFBReaper" % self.install_root,command],
+          cwd=self.directory,
+          stdout=subprocess.PIPE,
           stderr=subprocess.STDOUT)
           stderr=subprocess.STDOUT)
     nbsr = setup_util.NonBlockingStreamReader(p.stdout,
     nbsr = setup_util.NonBlockingStreamReader(p.stdout,
       "%s: %s.sh and framework processes have terminated" % (self.name, self.setup_file))
       "%s: %s.sh and framework processes have terminated" % (self.name, self.setup_file))
@@ -364,7 +347,7 @@ class FrameworkTest:
     logging.info("Executed %s.sh, returning %s", self.setup_file, retcode)
     logging.info("Executed %s.sh, returning %s", self.setup_file, retcode)
     os.chdir(previousDir)
     os.chdir(previousDir)
 
 
-    return retcode
+    return retcode, p
   ############################################################
   ############################################################
   # End start
   # End start
   ############################################################
   ############################################################

+ 2 - 9
toolset/run-tests.py

@@ -126,7 +126,6 @@ def main(argv=None):
     parser.add_argument('-s', '--server-host', default=serverHost, help='The application server.')
     parser.add_argument('-s', '--server-host', default=serverHost, help='The application server.')
     parser.add_argument('-c', '--client-host', default=clientHost, help='The client / load generation server.')
     parser.add_argument('-c', '--client-host', default=clientHost, help='The client / load generation server.')
     parser.add_argument('-u', '--client-user', default=clientUser, help='The username to use for SSH to the client instance.')
     parser.add_argument('-u', '--client-user', default=clientUser, help='The username to use for SSH to the client instance.')
-    parser.add_argument('-r', '--runner-user', default=runnerUser, help='The user to run each test as.')
     parser.add_argument('-i', '--client-identity-file', dest='client_identity_file', default=clientIden,
     parser.add_argument('-i', '--client-identity-file', dest='client_identity_file', default=clientIden,
                         help='The key to use for SSH to the client instance.')
                         help='The key to use for SSH to the client instance.')
     parser.add_argument('-d', '--database-host', default=databaHost,
     parser.add_argument('-d', '--database-host', default=databaHost,
@@ -176,16 +175,10 @@ def main(argv=None):
 
 
     # Verify and massage options
     # Verify and massage options
     if args.client_user is None:
     if args.client_user is None:
-      print 'Usernames (e.g. --client-user, --runner-user, and --database-user) are required!'
+      print 'Usernames (e.g. --client-user, and --database-user) are required!'
       print 'The system will SSH into the client and the database for the install stage'
       print 'The system will SSH into the client and the database for the install stage'
       print 'Aborting'
       print 'Aborting'
-      exit(1)
-
-    if args.runner_user is None:
-      print 'Usernames (e.g. --client-user, --runner-user, and --database-user) are required!'
-      print 'The system will run each test as the runner-user'
-      print 'Aborting'
-      exit(1)        
+      exit(1)    
 
 
     if args.database_user is None:
     if args.database_user is None:
       args.database_user = args.client_user
       args.database_user = args.client_user

+ 62 - 0
toolset/setup/linux/TFBReaper.c

@@ -0,0 +1,62 @@
+#define _DEFAULT_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+  // Gather the command line arguments for the pass-through.
+  int count = argc - 1;
+  int *sizes = malloc(sizeof(int) * count);
+  int total_size = 0;
+  for( int i = 1; i < argc; i++ ) {
+    sizes[i - 1] = strlen(argv[i]);
+    total_size += sizes[i - 1];
+  }
+  char *result = malloc(sizeof(char) * total_size + count);
+  char *ptr = result;
+  for( int i = 1; i < argc; i++ ) {
+    memcpy(ptr, argv[i], sizes[i - 1]);
+    ptr[sizes[i - 1]] = ' ';
+    ptr += sizes[i - 1] + 1;
+  }
+  *ptr = '\0';
+  free(sizes);
+
+  // Here is the magic. This sets any child processes to
+  // use THIS process as a 'subreaper'. What that means is
+  // even if the process uses the fork-exit technicque for
+  // running a daemon (which normally orphans the process
+  // and causes init(1) to adopt it, which is problematic
+  // for TFB because we cannot then generally kill the
+  // process since it has lost all context available to us)
+  // the child process will have the parent id of THIS
+  // process, allowing us to kill all the processes started
+  // by the suite in this way generally.
+  //
+  // See: http://man7.org/linux/man-pages/man2/prctl.2.html
+  prctl(PR_SET_CHILD_SUBREAPER,1);
+
+  // This invokes whatever was passed as arguments to TFBReaper
+  // on the system. This program is merely a pass-through to
+  // a shell with the subreaper stuff enabled.
+  int ret = system(result);
+
+  // We need to wait forever; the suite will clean this 
+  // process up later.
+  if (ret == 0) {
+    for(;;) { 
+      // Pause to keep us from spiking CPU; whenever a signal
+      // occurs (except SIGTERM etc which will kill this process)
+      // just iterate and pause again.
+      pause(); 
+    }
+  }
+
+  // If the scripts failed, we should return that code.
+  return ret;
+}
+

+ 1 - 1
toolset/setup/linux/frameworks/ffead-cpp-apache.sh

@@ -9,7 +9,7 @@ cd unixODBC-2.3.4
 ./configure --enable-stats=no --enable-gui=no --enable-drivers=no --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --sysconfdir=/etc
 ./configure --enable-stats=no --enable-gui=no --enable-drivers=no --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --sysconfdir=/etc
 sudo make install
 sudo make install
 
 
-sudo apt-get install build-essential
+sudo apt-get install -y build-essential
 sudo apt-get install -y uuid-dev libmyodbc odbc-postgresql
 sudo apt-get install -y uuid-dev libmyodbc odbc-postgresql
 
 
 fw_get -o ffead-cpp-2.0.tar.gz https://github.com/sumeetchhetri/ffead-cpp/releases/download/2.0/ffead-cpp-2.0-te-bin.tar.gz
 fw_get -o ffead-cpp-2.0.tar.gz https://github.com/sumeetchhetri/ffead-cpp/releases/download/2.0/ffead-cpp-2.0-te-bin.tar.gz

+ 1 - 1
toolset/setup/linux/frameworks/ffead-cpp-nginx.sh

@@ -9,7 +9,7 @@ cd unixODBC-2.3.4
 ./configure --enable-stats=no --enable-gui=no --enable-drivers=no --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --sysconfdir=/etc
 ./configure --enable-stats=no --enable-gui=no --enable-drivers=no --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --sysconfdir=/etc
 sudo make install
 sudo make install
 
 
-sudo apt-get install build-essential
+sudo apt-get install -y build-essential
 sudo apt-get install -y uuid-dev libmyodbc odbc-postgresql
 sudo apt-get install -y uuid-dev libmyodbc odbc-postgresql
 
 
 fw_get -o ffead-cpp-2.0.tar.gz https://github.com/sumeetchhetri/ffead-cpp/releases/download/2.0/ffead-cpp-2.0-te-bin.tar.gz
 fw_get -o ffead-cpp-2.0.tar.gz https://github.com/sumeetchhetri/ffead-cpp/releases/download/2.0/ffead-cpp-2.0-te-bin.tar.gz

+ 2 - 5
toolset/setup/linux/frameworks/ffead-cpp.sh

@@ -3,7 +3,7 @@
 RETCODE=$(fw_exists ${IROOT}/ffead-cpp.installed)
 RETCODE=$(fw_exists ${IROOT}/ffead-cpp.installed)
 [ ! "$RETCODE" == 0 ] || { return 0; }
 [ ! "$RETCODE" == 0 ] || { return 0; }
 
 
-sudo apt-get remove libodbc1 unixodbc unixodbc-dev
+sudo apt-get remove -y libodbc1 unixodbc unixodbc-dev
 
 
 fw_get -o unixODBC-2.3.4.tar.gz ftp://ftp.unixodbc.org/pub/unixODBC/unixODBC-2.3.4.tar.gz
 fw_get -o unixODBC-2.3.4.tar.gz ftp://ftp.unixodbc.org/pub/unixODBC/unixODBC-2.3.4.tar.gz
 fw_untar unixODBC-2.3.4.tar.gz
 fw_untar unixODBC-2.3.4.tar.gz
@@ -11,7 +11,7 @@ cd unixODBC-2.3.4
 ./configure --enable-stats=no --enable-gui=no --enable-drivers=no --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --sysconfdir=/etc
 ./configure --enable-stats=no --enable-gui=no --enable-drivers=no --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --sysconfdir=/etc
 sudo make install
 sudo make install
 
 
-sudo apt-get install build-essential
+sudo apt-get install -y build-essential
 sudo apt-get install -y uuid-dev libmyodbc odbc-postgresql
 sudo apt-get install -y uuid-dev libmyodbc odbc-postgresql
 
 
 fw_get -o ffead-cpp-2.0.tar.gz https://github.com/sumeetchhetri/ffead-cpp/releases/download/2.0/ffead-cpp-2.0-te-bin.tar.gz
 fw_get -o ffead-cpp-2.0.tar.gz https://github.com/sumeetchhetri/ffead-cpp/releases/download/2.0/ffead-cpp-2.0-te-bin.tar.gz
@@ -22,9 +22,6 @@ cp -R ffead-cpp-2.0-bin/ ${TROOT}
 mv ${TROOT}/ffead-cpp-2.0-bin ${TROOT}/ffead-cpp-2.0
 mv ${TROOT}/ffead-cpp-2.0-bin ${TROOT}/ffead-cpp-2.0
 rm -rf ffead-cpp-2.0/
 rm -rf ffead-cpp-2.0/
 
 
-sudo chown -R testrunner:testrunner ${TROOT}/ffead-cpp-2.0
-sudo chmod -R g+rw ${TROOT}/ffead-cpp-2.0
-
 sudo sed -i 's|localhost|'${DBHOST}'|g' ${TROOT}/ffead-cpp-2.0/web/te-benchmark/config/sdorm*
 sudo sed -i 's|localhost|'${DBHOST}'|g' ${TROOT}/ffead-cpp-2.0/web/te-benchmark/config/sdorm*
 
 
 sudo rm -f /etc/odbcinst.ini
 sudo rm -f /etc/odbcinst.ini

+ 1 - 1
toolset/setup/linux/frameworks/jester.sh

@@ -14,7 +14,7 @@ cd jester
 # 2015-06-25
 # 2015-06-25
 git checkout 71b8cc069a0d271d619c2dc41bc6479047885587
 git checkout 71b8cc069a0d271d619c2dc41bc6479047885587
 nimble update
 nimble update
-# If /home/testrunner/.nimble/pkgs/jester exists, write over it.
+# If ~/.nimble/pkgs/jester exists, write over it.
 echo 'y' | nimble install
 echo 'y' | nimble install
 
 
 echo "export JESTER_HOME=${JESTER}" > $IROOT/jester.installed
 echo "export JESTER_HOME=${JESTER}" > $IROOT/jester.installed

+ 2 - 2
toolset/setup/linux/languages/php5.sh

@@ -22,8 +22,8 @@ cd php5
 echo "Configuring PHP5 quietly..."
 echo "Configuring PHP5 quietly..."
 ./configure --prefix=$PHP_HOME --with-pdo-mysql \
 ./configure --prefix=$PHP_HOME --with-pdo-mysql \
   --with-mysql --with-mcrypt --enable-intl --enable-mbstring \
   --with-mysql --with-mcrypt --enable-intl --enable-mbstring \
-  --enable-fpm --with-fpm-user=testrunner --with-fpm-group=testrunner \
-  --with-openssl --with-mysqli --with-zlib --enable-opcache --quiet
+  --enable-fpm --with-openssl --with-mysqli --with-zlib \
+  --enable-opcache --quiet
 echo "Making PHP5 quietly..."
 echo "Making PHP5 quietly..."
 make --quiet
 make --quiet
 echo "Installing PHP5 quietly"
 echo "Installing PHP5 quietly"

+ 2 - 2
toolset/setup/linux/languages/php7.sh

@@ -17,8 +17,8 @@ cd php7
 echo "Configuring PHP quietly..."
 echo "Configuring PHP quietly..."
 ./configure --prefix=$PHP_HOME --with-pdo-mysql \
 ./configure --prefix=$PHP_HOME --with-pdo-mysql \
   --with-mcrypt --enable-intl --enable-mbstring \
   --with-mcrypt --enable-intl --enable-mbstring \
-  --enable-fpm --with-fpm-user=testrunner --with-fpm-group=testrunner \
-  --with-openssl --with-mysqli --with-zlib --enable-opcache --quiet
+  --enable-fpm --with-openssl --with-mysqli \
+  --with-zlib --enable-opcache --quiet
 echo "Making PHP quietly..."
 echo "Making PHP quietly..."
 make --quiet
 make --quiet
 echo "Installing PHP quietly"
 echo "Installing PHP quietly"

+ 1 - 2
toolset/setup/linux/prerequisites.sh

@@ -9,8 +9,7 @@ RETCODE=$(fw_exists fwbm_prereqs_installed)
 [ ! "$RETCODE" == 0 ] || { \
 [ ! "$RETCODE" == 0 ] || { \
   echo "Prerequisites installed!"; 
   echo "Prerequisites installed!"; 
   return 0; }
   return 0; }
-
-
+  
 # Use a more recent version of Mongo shell
 # Use a more recent version of Mongo shell
 sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
 sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
 echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
 echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list