diff --git a/test/boothtestenv.py b/test/boothtestenv.py index ba54360..7efef27 100644 --- a/test/boothtestenv.py +++ b/test/boothtestenv.py @@ -1,68 +1,73 @@ import os import subprocess import time import tempfile import unittest from assertions import BoothAssertions class BoothTestEnvironment(unittest.TestCase, BoothAssertions): test_src_path = os.path.abspath(os.path.dirname(__file__)) dist_path = os.path.join(test_src_path, '..' ) src_path = os.path.join(dist_path, 'src' ) boothd_path = os.path.join(src_path, 'boothd') conf_path = os.path.join(dist_path, 'conf' ) example_config_path = os.path.join(conf_path, 'booth.conf.example') def setUp(self): if not self._testMethodName.startswith('test_'): raise RuntimeError("unexpected test method name: " + self._testMethodName) self.test_name = self._testMethodName[5:] self.test_path = os.path.join(self.test_run_path, self.test_name) os.makedirs(self.test_path) + # Give all users permisions for temp directory so boothd running as "hacluster" + # can delete the lock file + if os.geteuid() == 0: + os.chmod(self.test_path, 0o777) + self.ensure_boothd_not_running() def ensure_boothd_not_running(self): # Need to redirect STDERR in case we're not root, in which # case netstat's -p option causes a warning. However we only # want to kill boothd processes which we own; -p will list the # pid for those and only those, which is exactly what we want # here. subprocess.call("netstat -tpln 2>&1 | perl -lne 'm,LISTEN\s+(\d+)/boothd, and kill 15, $1'", shell=True) def get_tempfile(self, identity): tf = tempfile.NamedTemporaryFile( prefix='%s.%d.' % (identity, time.time()), dir=self.test_path, delete=False ) return tf.name def init_log(self): self.log_file = self.get_tempfile('log') os.putenv('HA_debugfile', self.log_file) # See cluster-glue/lib/clplumbing/cl_log.c def read_log(self): if not os.path.exists(self.log_file): return '' l = open(self.log_file) msgs = ''.join(l.readlines()) l.close() return msgs def check_return_code(self, pid, return_code, expected_exitcode): if return_code is None: print("pid %d still running" % pid) if expected_exitcode is not None: self.fail("expected exit code %d, not long-running process" % expected_exitcode) else: print("pid %d exited with code %d" % (pid, return_code)) if expected_exitcode is None: msg = "should not exit" else: msg = "should exit with code %s" % expected_exitcode msg += "\nLog follows (see %s)" % self.log_file msg += "\nN.B. expect mlockall/setscheduler errors when running tests non-root" msg += "\n-----------\n%s" % self.read_log() self.assertEqual(return_code, expected_exitcode, msg) diff --git a/test/runtests.py.in b/test/runtests.py.in index 312cf92..45100ab 100644 --- a/test/runtests.py.in +++ b/test/runtests.py.in @@ -1,72 +1,77 @@ #!@PYTHON_SHEBANG@ import os import shutil import sys import tempfile import time import unittest from clienttests import ClientConfigTests from sitetests import SiteConfigTests #from arbtests import ArbitratorConfigTests if __name__ == '__main__': # Likely assumption for the root exclusion is the amount of risk # associated with what naturally accompanies root privileges: # - accidental overwrite (eventually also deletion) of unrelated, # legitimate and perhaps vital files # - accidental termination of unrelated, legitimate and perhaps # vital processes # - and so forth, possibly amplified with awkward parallel test # suite run scenarios (containers partly sharing state, etc.) # # Nonetheless, there are cases like self-contained CI runs where # all these concerns are absent, so allow opt-in relaxing of this. # Alternatively, the config generator could inject particular # credentials for a booth proces to use, but that might come too # late to address the above concerns reliably. if (os.geteuid() == 0 and "--allow-root-user" not in sys.argv and not(os.environ.get("BOOTH_RUNTESTS_ROOT_USER"))): sys.stderr.write("Must be run non-root; aborting.\n") sys.exit(1) tmp_path = '/tmp/booth-tests' if not os.path.exists(tmp_path): os.makedirs(tmp_path) test_run_path = tempfile.mkdtemp(prefix='%d.' % time.time(), dir=tmp_path) + if os.geteuid() == 0: + # Give all users at least rx permisions for temp directory so hacluster running booth + # can delete lock file + os.chmod(test_run_path, 0o755) suite = unittest.TestSuite() testclasses = [ SiteConfigTests, #ArbitratorConfigTests, ClientConfigTests, ] for testclass in testclasses: testclass.test_run_path = test_run_path suite.addTests(unittest.TestLoader().loadTestsFromTestCase(testclass)) runner_args = { #'verbosity' : 2, } major, minor, micro, releaselevel, serial = sys.version_info if major > 2 or (major == 2 and minor >= 7): # New in 2.7 runner_args['buffer'] = True runner_args['failfast'] = True pass - # not root anymore, so safe - # needed because old instances might still use the UDP port. - os.system("killall boothd") + if os.geteuid() != 0: + # not root, so safe + # needed because old instances might still use the UDP port. + os.system("killall boothd") runner = unittest.TextTestRunner(**runner_args) result = runner.run(suite) if result.wasSuccessful(): shutil.rmtree(test_run_path) sys.exit(0) else: print("Left %s for debugging" % test_run_path) sys.exit(1)