diff --git a/.booth.metadata b/.booth.metadata new file mode 100644 index 0000000..c852833 --- /dev/null +++ b/.booth.metadata @@ -0,0 +1 @@ +1c6b69921dc435310094b53f4555d60c95889a19 SOURCES/booth-f2d38ce.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3e818f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/booth-f2d38ce.tar.gz diff --git a/SOURCES/0000-test-remove-superfluous-shebangs-for-import-only-mod.patch b/SOURCES/0000-test-remove-superfluous-shebangs-for-import-only-mod.patch new file mode 100644 index 0000000..d53772f --- /dev/null +++ b/SOURCES/0000-test-remove-superfluous-shebangs-for-import-only-mod.patch @@ -0,0 +1,158 @@ +From 9469ffc2d58a5673fffae8778b9c48f5605dda6a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Tue, 10 Jul 2018 18:41:18 +0200 +Subject: [PATCH] test: remove superfluous shebangs for import-only modules +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Consequently, there's no reason to have the affected files marked as +executable. + +Signed-off-by: Jan Pokorný +--- + test/arbtests.py | 2 -- + test/assertions.py | 2 -- + test/boothrunner.py | 2 -- + test/boothtestenv.py | 2 -- + test/clientenv.py | 2 -- + test/clienttests.py | 2 -- + test/serverenv.py | 2 -- + test/servertests.py | 2 -- + test/sitetests.py | 2 -- + test/utils.py | 2 -- + 10 files changed, 20 deletions(-) + mode change 100755 => 100644 test/arbtests.py + mode change 100755 => 100644 test/assertions.py + mode change 100755 => 100644 test/boothrunner.py + mode change 100755 => 100644 test/boothtestenv.py + mode change 100755 => 100644 test/clientenv.py + mode change 100755 => 100644 test/clienttests.py + mode change 100755 => 100644 test/serverenv.py + mode change 100755 => 100644 test/servertests.py + mode change 100755 => 100644 test/sitetests.py + mode change 100755 => 100644 test/utils.py + +diff --git a/test/arbtests.py b/test/arbtests.py +old mode 100755 +new mode 100644 +index caba010..ef7b7f9 +--- a/test/arbtests.py ++++ b/test/arbtests.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + from servertests import ServerTests + + class ArbitratorConfigTests(ServerTests): +diff --git a/test/assertions.py b/test/assertions.py +old mode 100755 +new mode 100644 +index 4396ab7..0b7f995 +--- a/test/assertions.py ++++ b/test/assertions.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + import re + + class BoothAssertions: +diff --git a/test/boothrunner.py b/test/boothrunner.py +old mode 100755 +new mode 100644 +index f9154e7..d981183 +--- a/test/boothrunner.py ++++ b/test/boothrunner.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + import os + import subprocess + import time +diff --git a/test/boothtestenv.py b/test/boothtestenv.py +old mode 100755 +new mode 100644 +index 89a484a..fcd0c4d +--- a/test/boothtestenv.py ++++ b/test/boothtestenv.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + import os + import subprocess + import time +diff --git a/test/clientenv.py b/test/clientenv.py +old mode 100755 +new mode 100644 +index fcd40fa..73b2791 +--- a/test/clientenv.py ++++ b/test/clientenv.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + from boothtestenv import BoothTestEnvironment + from boothrunner import BoothRunner + +diff --git a/test/clienttests.py b/test/clienttests.py +old mode 100755 +new mode 100644 +index 61b691b..c4b9d8a +--- a/test/clienttests.py ++++ b/test/clienttests.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + import string + + from clientenv import ClientTestEnvironment +diff --git a/test/serverenv.py b/test/serverenv.py +old mode 100755 +new mode 100644 +index d0467b9..c6d4e30 +--- a/test/serverenv.py ++++ b/test/serverenv.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + import os + import re + import time +diff --git a/test/servertests.py b/test/servertests.py +old mode 100755 +new mode 100644 +index f574f26..39a6ffc +--- a/test/servertests.py ++++ b/test/servertests.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + import copy + from pprint import pprint, pformat + import re +diff --git a/test/sitetests.py b/test/sitetests.py +old mode 100755 +new mode 100644 +index dfdf6b9..6944ffe +--- a/test/sitetests.py ++++ b/test/sitetests.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + from servertests import ServerTests + + class SiteConfigTests(ServerTests): +diff --git a/test/utils.py b/test/utils.py +old mode 100755 +new mode 100644 +index ceeef98..5b70cfc +--- a/test/utils.py ++++ b/test/utils.py +@@ -1,5 +1,3 @@ +-#!/usr/bin/python +- + import subprocess + import re + +-- +2.18.0.rc2 + diff --git a/SOURCES/0001-test-do-not-mix-tabs-with-spaces-in-Python-code.patch b/SOURCES/0001-test-do-not-mix-tabs-with-spaces-in-Python-code.patch new file mode 100644 index 0000000..e495122 --- /dev/null +++ b/SOURCES/0001-test-do-not-mix-tabs-with-spaces-in-Python-code.patch @@ -0,0 +1,60 @@ +From a642a833e31a6bd1e71dc2045a16e494775b35e6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Thu, 12 Jul 2018 18:58:32 +0200 +Subject: [PATCH] test: do not mix tabs with spaces in Python code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Beside being matter of a good style, it's also forbidden inside +a single, non-delimited block in Python 3. + +Signed-off-by: Jan Pokorný +--- + test/servertests.py | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/test/servertests.py b/test/servertests.py +index 39a6ffc..71e808e 100644 +--- a/test/servertests.py ++++ b/test/servertests.py +@@ -51,8 +51,8 @@ class ServerTests(ServerTestEnvironment): + config_text=self.working_config) + + def test_missing_quotes(self): +- # quotes no longer required +- return True ++ # quotes no longer required ++ return True + orig_lines = self.working_config.split("\n") + for i in xrange(len(orig_lines)): + new_lines = copy.copy(orig_lines) +@@ -97,8 +97,8 @@ class ServerTests(ServerTestEnvironment): + expected_exitcode=None, expected_daemon=True) + + def test_missing_transport(self): +- # UDP is default -- TODO? +- return True ++ # UDP is default -- TODO? ++ return True + config = re.sub('transport=.+\n', '', self.typical_config) + (pid, ret, stdout, stderr, runner) = \ + self.run_booth(config_text=config, expected_exitcode=1, expected_daemon=False) +@@ -141,10 +141,10 @@ class ServerTests(ServerTestEnvironment): + self.assertRegexpMatches(stderr, 'ticket name "' + ticket + '" invalid') + + def test_unreachable_peer(self): +- # what should this test do? daemon not expected, but no exitcode either? +- # booth would now just run, and try to reach that peer... +- # TCP reachability is not required during startup anymore. +- return True ++ # what should this test do? daemon not expected, but no exitcode either? ++ # booth would now just run, and try to reach that peer... ++ # TCP reachability is not required during startup anymore. ++ return True + config = re.sub('#(.+147.+)', lambda m: m.group(1), self.working_config) + self.run_booth(config_text=config, + expected_exitcode=None, expected_daemon=False) +-- +2.18.0.rc2 + diff --git a/SOURCES/0002-test-make-Python-files-supported-_also_-with-Python-.patch b/SOURCES/0002-test-make-Python-files-supported-_also_-with-Python-.patch new file mode 100644 index 0000000..f366aa8 --- /dev/null +++ b/SOURCES/0002-test-make-Python-files-supported-_also_-with-Python-.patch @@ -0,0 +1,510 @@ +From ab2229451827f530959d554920619d87daa34586 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Wed, 11 Jul 2018 16:18:25 +0200 +Subject: [PATCH] test: make Python files supported _also_ with Python 3.3+ +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +- use "print" like a function rather than a statement + . where implicit newline is to be suppressed, don't do that + and rather strip it from the string to be printed instead +- use 2+3 compatible convention for parametrizing exceptions +- Python 3 doesn't recognize "basestring" class, and at the place + of use (pre Python 2.7 only), unicode string is really not expected + (also re.UNICODE flag is not used...) +- Python 3 doesn't recognize "xrange" function, but the surrounding + code can be reasonably simplified using "enumerate" function +- arrange dict treatment in a compatible way: + . d.has_key(k) -> k in d + . d.iteritems() -> custom "iter_items", always efficient wrapper + . d.iterkeys(), here incl. lazy mapping and filtering + -> rewrite while retaining laziness + . optimize UT.merge_dicts in script/unit-test.py along +- also in three instances, deal with string/uninterpreted bytes proper + dichotomy introduced in Python 3, and related to that, "string" + module only supports "ascii_lowercase" attribute in Python 3 + (as opposed to system-specific plain "lowercase" one) + +Note that script/unit-test.py has a pre-existing issue (regardless +of which Python version interpreter is used), so at least document +that in the header for now. + +Signed-off-by: Jan Pokorný +--- + script/unit-test.py | 65 ++++++++++++++++++++++++++------------------ + test/assertions.py | 4 +-- + test/boothrunner.py | 32 ++++++++++++---------- + test/boothtestenv.py | 6 ++-- + test/clienttests.py | 4 +-- + test/runtests.py | 2 +- + test/serverenv.py | 18 ++++++------ + test/servertests.py | 10 +++---- + test/utils.py | 10 +++++-- + 9 files changed, 84 insertions(+), 67 deletions(-) + +diff --git a/script/unit-test.py b/script/unit-test.py +index 6871930..399528e 100755 +--- a/script/unit-test.py ++++ b/script/unit-test.py +@@ -1,6 +1,8 @@ + #!/usr/bin/python + # vim: fileencoding=utf-8 + # see http://stackoverflow.com/questions/728891/correct-way-to-define-python-source-code-encoding ++# NOTE: setting the encoding is needed as non-ASCII characters are contained ++# FIXME: apparently, pexpect.EOF is not being excepted properly + + import os, sys, time, signal, tempfile, socket, posix, time + import re, shutil, pexpect, logging, pprint +@@ -16,6 +18,16 @@ default_log_format = '%(asctime)s: : %(message)s' + default_log_datefmt = '%b %d %H:%M:%S' + + ++# Compatibility with dictionary methods not present in Python 3; ++# https://www.python.org/dev/peps/pep-0469/#migrating-to-the-common-subset-of-python-2-and-3 ++try: ++ dict.iteritems ++except AttributeError: # Python 3 ++ iter_items = lambda d: iter(d.items()) ++else: # Python 2 ++ iter_items = lambda d: d.iteritems() ++ ++ + # {{{ pexpect-logging glue + # needed for use as pexpect.logfile, to relay into existing logfiles + class expect_logging(): +@@ -28,9 +40,12 @@ class expect_logging(): + + def flush(self, *arg): + pass ++ + def write(self, stg): + if self.test.dont_log_expect == 0: + # TODO: split by input/output, give program ++ if sys.version_info[0] >= 3: ++ stg = str(stg, 'UTF-8') + for line in re.split(r"[\r\n]+", stg): + if line == self.test.prompt: + continue +@@ -110,7 +125,7 @@ class UT(): + res = re.match(r"^\s*(\w+)\s*:(?:\s*(#.*?\S))?\s*$", line) + if res: + state = res.group(1) +- if not m.has_key(state): ++ if state not in m: + m[state] = dict_plus() + if res.group(2): + m[state].aux["comment"] = res.group(2) +@@ -188,17 +203,15 @@ class UT(): + name = re.sub(r".*/", "", bin) + # How to get stderr, too? + expct = pexpect.spawn(bin, +- env = dict( os.environ.items() + +- [('PATH', +- self.test_base + "/bin/:" + +- os.getenv('PATH')), +- ('UNIT_TEST_PATH', self.test_base), +- ('LC_ALL', 'C'), +- ('LANG', 'C')] + +- env_add ), +- timeout = 30, +- maxread = 32768, +- **args) ++ env=dict(os.environ, **dict({ ++ 'PATH': ':'.join((self.test_base + "/bin/", ++ os.getenv('PATH'))), ++ 'UNIT_TEST_PATH': self.test_base, ++ 'LC_ALL': 'C', ++ 'LANG': 'C'}, **dict(env_add))), ++ timeout=30, ++ maxread=32768, ++ **args) + expct.setecho(False) + expct.logfile_read = expect_logging("<- %s" % name, self) + expct.logfile_send = expect_logging(" -> %s" % name, self) +@@ -361,7 +374,7 @@ class UT(): + + self.current_nr = kv.aux.get("line") + #os.system("strace -f -tt -s 2000 -e write -p" + str(self.gdb.pid) + " &") +- for n, v in kv.iteritems(): ++ for n, v in iter_items(kv): + self.set_val( self.translate_shorthand(n, "ticket"), v) + logging.info("set state") + +@@ -372,7 +385,7 @@ class UT(): + if not sys.stdin.isatty(): + logging.error("Not a terminal, stopping.") + else: +- print "\n\nEntering interactive mode.\n\n" ++ print("\n\nEntering interactive mode.\n\n") + self.gdb.sendline("set prompt GDB> \n") + self.gdb.setecho(True) + # can't use send_cmd, doesn't reply with expected prompt anymore. +@@ -415,7 +428,7 @@ class UT(): + self.send_cmd("next") + + # push message. +- for (n, v) in msg.iteritems(): ++ for (n, v) in iter_items(msg): + self.set_val( self.translate_shorthand(n, "message"), v, "htonl") + + # set "received" length +@@ -426,7 +439,7 @@ class UT(): + def wait_outgoing(self, msg): + self.wait_for_function("booth_udp_send") + ok = True +- for (n, v) in msg.iteritems(): ++ for (n, v) in iter_items(msg): + if re.search(r"\.", n): + ok = self.check_value( self.translate_shorthand(n, "inject"), v) and ok + else: +@@ -438,14 +451,12 @@ class UT(): + #stopped_at = self.sync() + + def merge_dicts(self, base, overlay): +- return dict(base.items() + overlay.items()) ++ return dict(base, **overlay) + + + def loop(self, fn, data): +- matches = map(lambda k: re.match(r"^(outgoing|message)(\d+)$", k), data.iterkeys()) +- valid_matches = filter(None, matches) +- nums = map(lambda m: int(m.group(2)), valid_matches) +- loop_max = max(nums) ++ matches = (re.match(r"^(outgoing|message)(\d+)$", k) for k in data) ++ loop_max = max(int(m.group(2)) for m in matches if m) + for counter in range(0, loop_max+1): # incl. last message + + kmsg = 'message%d' % counter +@@ -471,14 +482,14 @@ class UT(): + logging.info("ticket change %s (%s:%d) %s" % (ktkt, fn, self.current_nr, comment)) + self.set_state(tkt) + if gdb: +- for (k, v) in gdb.iteritems(): ++ for (k, v) in iter_items(gdb): + self.send_cmd(k + " " + v.replace("§", "\n")) + if msg: + self.current_nr = msg.aux.get("line") + comment = msg.aux.get("comment", "") + logging.info("sending %s (%s:%d) %s" % (kmsg, fn, self.current_nr, comment)) + self.send_message(self.merge_dicts(data["message"], msg)) +- if data.has_key(kgdb) and len(gdb) == 0: ++ if kgdb in data and len(gdb) == 0: + self.user_debug("manual override") + if out: + self.current_nr = out.aux.get("line") +@@ -520,7 +531,7 @@ class UT(): + self.let_booth_go_a_bit() + + ok = True +- for (n, v) in data.iteritems(): ++ for (n, v) in iter_items(data): + ok = self.check_value( self.translate_shorthand(n, "ticket"), v) and ok + if not ok: + sys.exit(1) +@@ -529,8 +540,8 @@ class UT(): + def run(self, start_from="000", end_with="999"): + os.chdir(self.test_base) + # TODO: sorted, random order +- tests = filter( (lambda f: re.match(r"^\d\d\d_.*\.txt$", f)), glob.glob("*")) +- tests.sort() ++ tests = sorted(f for f in glob.glob("*") ++ if re.match(r"^\d\d\d_.*\.txt$", f)) + failed = 0 + for f in tests: + if f[0:3] < start_from: +@@ -561,7 +572,7 @@ class UT(): + except: + failed += 1 + logging.error(self.colored_string("Broke in %s:%s %s" % (f, self.current_nr, sys.exc_info()), self.RED)) +- for frame in traceback.format_tb(sys.exc_traceback): ++ for frame in traceback.format_tb(sys.exc_info()[2]): + logging.info(" - %s " % frame.rstrip()) + finally: + self.stop_processes() +diff --git a/test/assertions.py b/test/assertions.py +index 0b7f995..34333ca 100644 +--- a/test/assertions.py ++++ b/test/assertions.py +@@ -21,7 +21,7 @@ class BoothAssertions: + # backported from 2.7 just in case we're running on an older Python + def assertRegexpMatches(self, text, expected_regexp, msg=None): + """Fail the test unless the text matches the regular expression.""" +- if isinstance(expected_regexp, basestring): ++ if isinstance(expected_regexp, str): + expected_regexp = re.compile(expected_regexp) + if not expected_regexp.search(text, MULTILINE): + msg = msg or "Regexp didn't match" +@@ -30,7 +30,7 @@ class BoothAssertions: + + def assertNotRegexpMatches(self, text, unexpected_regexp, msg=None): + """Fail the test if the text matches the regular expression.""" +- if isinstance(unexpected_regexp, basestring): ++ if isinstance(unexpected_regexp, str): + unexpected_regexp = re.compile(unexpected_regexp) + match = unexpected_regexp.search(text) + if match: +diff --git a/test/boothrunner.py b/test/boothrunner.py +index d981183..347912b 100644 +--- a/test/boothrunner.py ++++ b/test/boothrunner.py +@@ -1,4 +1,5 @@ + import os ++import sys + import subprocess + import time + import unittest +@@ -37,14 +38,14 @@ class BoothRunner: + + def show_output(self, stdout, stderr): + if stdout: +- print "STDOUT:" +- print "------" +- print stdout, ++ print("STDOUT:") ++ print("------") ++ print(stdout.rstrip('\n')) + if stderr: +- print "STDERR: (N.B. crm_ticket failures indicate daemon started correctly)" +- print "------" +- print stderr, +- print "-" * 70 ++ print("STDERR: (N.B. crm_ticket failures indicate daemon started correctly)") ++ print("------") ++ print(stderr.rstrip('\n')) ++ print("-" * 70) + + def subproc_completed_within(self, p, timeout): + start = time.time() +@@ -55,7 +56,7 @@ class BoothRunner: + elapsed = time.time() - start + if elapsed + wait > timeout: + wait = timeout - elapsed +- print "Waiting on %d for %.1fs ..." % (p.pid, wait) ++ print("Waiting on %d for %.1fs ..." % (p.pid, wait)) + time.sleep(wait) + elapsed = time.time() - start + if elapsed >= timeout: +@@ -83,26 +84,29 @@ class BoothRunner: + return text + + def show_args(self): +- print "\n" +- print "-" * 70 +- print "Running", ' '.join(self.all_args()) ++ print("\n") ++ print("-" * 70) ++ print("Running", ' '.join(self.all_args())) + msg = "with config from %s" % self.config_file_used() + config_text = self.config_text_used() + if config_text is not None: + msg += ": [%s]" % config_text +- print msg ++ print(msg) + + def run(self): + p = subprocess.Popen(self.all_args(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if not p: +- raise RuntimeError, "failed to start subprocess" ++ raise RuntimeError("failed to start subprocess") + +- print "Started subprocess pid %d" % p.pid ++ print("Started subprocess pid %d" % p.pid) + + completed = self.subproc_completed_within(p, 2) + + if completed: + (stdout, stderr) = p.communicate() ++ if sys.version_info[0] >= 3: ++ # only expect ASCII/UTF-8 encodings for the obtained input bytes ++ stdout, stderr = str(stdout, 'UTF-8'), str(stderr, 'UTF-8') + self.show_output(stdout, stderr) + return (p.pid, p.returncode, stdout, stderr) + +diff --git a/test/boothtestenv.py b/test/boothtestenv.py +index fcd0c4d..59e25c3 100644 +--- a/test/boothtestenv.py ++++ b/test/boothtestenv.py +@@ -17,7 +17,7 @@ class BoothTestEnvironment(unittest.TestCase, BoothAssertions): + + def setUp(self): + if not self._testMethodName.startswith('test_'): +- raise RuntimeError, "unexpected test method name: " + self._testMethodName ++ 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) +@@ -54,11 +54,11 @@ class BoothTestEnvironment(unittest.TestCase, BoothAssertions): + + def check_return_code(self, pid, return_code, expected_exitcode): + if return_code is None: +- print "pid %d still running" % pid ++ 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) ++ print("pid %d exited with code %d" % (pid, return_code)) + if expected_exitcode is None: + msg = "should not exit" + else: +diff --git a/test/clienttests.py b/test/clienttests.py +index c4b9d8a..512620e 100644 +--- a/test/clienttests.py ++++ b/test/clienttests.py +@@ -7,14 +7,14 @@ class ClientConfigTests(ClientTestEnvironment): + + def test_site_buffer_overflow(self): + # https://bugzilla.novell.com/show_bug.cgi?id=750256 +- longfile = (string.lowercase * 3)[:63] ++ longfile = (string.ascii_lowercase * 3)[:63] + expected_error = "'%s' exceeds maximum site name length" % longfile + args = [ 'grant', '-s', longfile, '-t', 'ticket' ] + self._test_buffer_overflow(expected_error, args=args) + + def test_ticket_buffer_overflow(self): + # https://bugzilla.novell.com/show_bug.cgi?id=750256 +- longfile = (string.lowercase * 3)[:63] ++ longfile = (string.ascii_lowercase * 3)[:63] + expected_error = "'%s' exceeds maximum ticket name length" % longfile + args = [ 'grant', '-s', 'site', '-t', longfile ] + self._test_buffer_overflow(expected_error, args=args) +diff --git a/test/runtests.py b/test/runtests.py +index 0532c01..833b1a7 100755 +--- a/test/runtests.py ++++ b/test/runtests.py +@@ -53,5 +53,5 @@ if __name__ == '__main__': + shutil.rmtree(test_run_path) + sys.exit(0) + else: +- print "Left %s for debugging" % test_run_path ++ print("Left %s for debugging" % test_run_path) + sys.exit(1) +diff --git a/test/serverenv.py b/test/serverenv.py +index c6d4e30..5d6c6c4 100644 +--- a/test/serverenv.py ++++ b/test/serverenv.py +@@ -73,12 +73,10 @@ ticket="ticketB" + where return_code/stdout/stderr are None iff pid is still running. + ''' + if expected_daemon and expected_exitcode is not None and expected_exitcode != 0: +- raise RuntimeError, \ +- "Shouldn't ever expect daemon to start and then failure" ++ raise RuntimeError("Shouldn't ever expect daemon to start and then failure") + + if not expected_daemon and expected_exitcode == 0: +- raise RuntimeError, \ +- "Shouldn't ever expect success without starting daemon" ++ raise RuntimeError("Shouldn't ever expect success without starting daemon") + + self.init_log() + +@@ -122,9 +120,9 @@ ticket="ticketB" + return config_file + + def kill_pid(self, pid): +- print "killing %d ..." % pid ++ print("killing %d ..." % pid) + os.kill(pid, 15) +- print "killed" ++ print("killed") + + def check_daemon_handling(self, runner, expected_daemon): + ''' +@@ -154,7 +152,7 @@ ticket="ticketB" + Returns the pid contained in lock_file, or None if it doesn't exist. + ''' + if not os.path.exists(lock_file): +- print "%s does not exist" % lock_file ++ print("%s does not exist" % lock_file) + return None + + l = open(lock_file) +@@ -162,7 +160,7 @@ ticket="ticketB" + l.close() + self.assertEqual(len(lines), 1, "Lock file should contain one line") + pid = re.search('\\bbooth_pid="?(\\d+)"?', lines[0]).group(1) +- print "lockfile contains: <%s>" % pid ++ print("lockfile contains: <%s>" % pid) + return pid + + def is_pid_running_daemon(self, pid): +@@ -185,11 +183,11 @@ ticket="ticketB" + + c = open("/proc/%s/cmdline" % pid) + cmdline = "".join(c.readlines()) +- print cmdline ++ print(cmdline) + c.close() + + if cmdline.find('boothd') == -1: +- print 'no boothd in cmdline:', cmdline ++ print('no boothd in cmdline:', cmdline) + return False + + # self.assertRegexpMatches( +diff --git a/test/servertests.py b/test/servertests.py +index 71e808e..288d19f 100644 +--- a/test/servertests.py ++++ b/test/servertests.py +@@ -35,13 +35,13 @@ class ServerTests(ServerTestEnvironment): + + def test_config_file_buffer_overflow(self): + # https://bugzilla.novell.com/show_bug.cgi?id=750256 +- longfile = (string.lowercase * 5)[:127] ++ longfile = (string.ascii_lowercase * 5)[:127] + expected_error = "'%s' exceeds maximum config name length" % longfile + self._test_buffer_overflow(expected_error, config_file=longfile) + + def test_lock_file_buffer_overflow(self): + # https://bugzilla.novell.com/show_bug.cgi?id=750256 +- longfile = (string.lowercase * 5)[:127] ++ longfile = (string.ascii_lowercase * 5)[:127] + expected_error = "'%s' exceeds maximum lock file length" % longfile + self._test_buffer_overflow(expected_error, lock_file=longfile) + +@@ -54,12 +54,12 @@ class ServerTests(ServerTestEnvironment): + # quotes no longer required + return True + orig_lines = self.working_config.split("\n") +- for i in xrange(len(orig_lines)): ++ for (i, line) in enumerate(orig_lines): + new_lines = copy.copy(orig_lines) +- new_lines[i] = new_lines[i].replace('"', '') ++ new_lines[i] = line.replace('"', '') + new_config = "\n".join(new_lines) + +- line_contains_IP = re.search('^\s*(site|arbitrator)=.*[0-9]\.', orig_lines[i]) ++ line_contains_IP = re.search('^\s*(site|arbitrator)=.*[0-9]\.', line) + if line_contains_IP: + # IP addresses need to be surrounded by quotes, + # so stripping them should cause it to fail +diff --git a/test/utils.py b/test/utils.py +index 5b70cfc..aca3592 100644 +--- a/test/utils.py ++++ b/test/utils.py +@@ -1,5 +1,6 @@ + import subprocess + import re ++import sys + + def run_cmd(cmd): + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +@@ -9,8 +10,11 @@ def run_cmd(cmd): + def get_IP(): + (stdout, stderr, returncode) = run_cmd(['hostname', '-i']) + if returncode != 0: +- raise RuntimeError, "Failed to run hostname -i:\n" + stderr ++ raise RuntimeError("Failed to run hostname -i:\n" + stderr) + # in case multiple IP addresses are returned, use only the first +- # and also strip '%' part possibly present with IPv6 address +- ret = re.sub(r'\s.*', '', stdout) ++ # and also strip '%' part possibly present with IPv6 address; ++ # in Python 3 context, only expect ASCII/UTF-8 encodings for the ++ # obtained input bytes ++ ret = re.sub(r'\s.*', '', ++ stdout if sys.version_info[0] < 3 else str(stdout, 'UTF-8')) + return "::1" if '%' in ret else ret +-- +2.18.0.rc2 + diff --git a/SOURCES/0003-build-parametrize-Python-invocations-in-the-shebangs.patch b/SOURCES/0003-build-parametrize-Python-invocations-in-the-shebangs.patch new file mode 100644 index 0000000..a3ce25f --- /dev/null +++ b/SOURCES/0003-build-parametrize-Python-invocations-in-the-shebangs.patch @@ -0,0 +1,89 @@ +From 31133e8ac07c08b607ee7799c0074c1dce37a952 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Wed, 11 Jul 2018 14:18:50 +0200 +Subject: [PATCH] build: parametrize Python invocations in the shebangs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Consequently, there's no reason to have the affected files marked as +executable (processing the files by the means of AC_CONFIG_FILES will +get rid of any such permission bits, anyway), but at the very least, +test/runtests.py needs to be set executable afterwards so as no to +cause failures with the current "make check" arrangement that invokes +TESTS subtargets directly (i.e. no extension-based LOG_COMPILER set). + +Signed-off-by: Jan Pokorný +--- + configure.ac | 12 +++++++++++- + script/{unit-test.py => unit-test.py.in} | 2 +- + test/{runtests.py => runtests.py.in} | 2 +- + 3 files changed, 13 insertions(+), 3 deletions(-) + rename script/{unit-test.py => unit-test.py.in} (99%) + mode change 100755 => 100644 + rename test/{runtests.py => runtests.py.in} (98%) + mode change 100755 => 100644 + +diff --git a/configure.ac b/configure.ac +index 3bf41b3..a6ad86e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -97,6 +97,14 @@ CPPFLAGS="$CPPFLAGS $XML2HEAD" + + PKG_CHECK_MODULES(GLIB, [glib-2.0]) + ++# Python casing, prefer 3.3+ to 2.{6...} ++AM_PATH_PYTHON([3.3], , [PYTHON=:]) ++if test "x$PYTHON" = x:; then ++ AM_PATH_PYTHON([2.6]) ++fi ++PYTHON_SHEBANG="$PYTHON ${PYTHON_OPTS--Es}" ++AC_ARG_VAR([PYTHON_SHEBANG], [Python invocation used in shebangs]) ++ + # Checks for header files. + AC_FUNC_ALLOCA + AC_HEADER_DIRENT +@@ -157,7 +165,9 @@ AC_CONFIG_FILES([Makefile + docs/Makefile + conf/Makefile]) + AC_CONFIG_FILES([conf/booth-arbitrator.service conf/booth@.service]) +- ++AC_CONFIG_FILES([script/unit-test.py test/runtests.py], ++ dnl Following required at least for "make check" ++ [chmod +x test/runtests.py]) + AC_CONFIG_FILES([script/service-runnable], [chmod +x script/service-runnable]) + + # =============================================== +diff --git a/script/unit-test.py b/script/unit-test.py.in +old mode 100755 +new mode 100644 +similarity index 99% +rename from script/unit-test.py +rename to script/unit-test.py.in +index 399528e..4f3cf62 +--- a/script/unit-test.py ++++ b/script/unit-test.py.in +@@ -1,4 +1,4 @@ +-#!/usr/bin/python ++#!@PYTHON_SHEBANG@ + # vim: fileencoding=utf-8 + # see http://stackoverflow.com/questions/728891/correct-way-to-define-python-source-code-encoding + # NOTE: setting the encoding is needed as non-ASCII characters are contained +diff --git a/test/runtests.py b/test/runtests.py.in +old mode 100755 +new mode 100644 +similarity index 98% +rename from test/runtests.py +rename to test/runtests.py.in +index 833b1a7..ec59159 +--- a/test/runtests.py ++++ b/test/runtests.py.in +@@ -1,4 +1,4 @@ +-#!/usr/bin/python ++#!@PYTHON_SHEBANG@ + + import os + import re +-- +2.18.0.rc2 + diff --git a/SOURCES/0004-test-drop-underqualified-identifier-in-unittest-s-2..patch b/SOURCES/0004-test-drop-underqualified-identifier-in-unittest-s-2..patch new file mode 100644 index 0000000..b337059 --- /dev/null +++ b/SOURCES/0004-test-drop-underqualified-identifier-in-unittest-s-2..patch @@ -0,0 +1,36 @@ +From 541e6184fca60a01ff7e8c1bba794c083ac4245f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Tue, 10 Jul 2018 19:25:34 +0200 +Subject: [PATCH] test: drop underqualified identifier in unittest's 2.6 compat + "polyfill" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Specifically, in supplemented unittest.TestCase.assertRegexpMatches +method. In Python 2.7's implementation, there's no re.MULTILINE +modifier at that very place, either. + +Not sure what the original purpose of introducing that with c1c47f5 was. + +Signed-off-by: Jan Pokorný +--- + test/assertions.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/assertions.py b/test/assertions.py +index 34333ca..fafb291 100644 +--- a/test/assertions.py ++++ b/test/assertions.py +@@ -23,7 +23,7 @@ class BoothAssertions: + """Fail the test unless the text matches the regular expression.""" + if isinstance(expected_regexp, str): + expected_regexp = re.compile(expected_regexp) +- if not expected_regexp.search(text, MULTILINE): ++ if not expected_regexp.search(text): + msg = msg or "Regexp didn't match" + msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text) + raise self.failureException(msg) +-- +2.18.0.rc2 + diff --git a/SOURCES/0005-test-drop-comment-out-superfluous-imports.patch b/SOURCES/0005-test-drop-comment-out-superfluous-imports.patch new file mode 100644 index 0000000..45adf33 --- /dev/null +++ b/SOURCES/0005-test-drop-comment-out-superfluous-imports.patch @@ -0,0 +1,90 @@ +From 6a6834a8110d9e6aff50cd6d6935976af4cbdb8f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Thu, 12 Jul 2018 20:18:07 +0200 +Subject: [PATCH] test: drop/comment out superfluous imports +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Jan Pokorný +--- + test/boothrunner.py | 2 -- + test/boothtestenv.py | 1 - + test/runtests.py.in | 3 +-- + test/serverenv.py | 2 -- + test/servertests.py | 1 - + 5 files changed, 1 insertion(+), 8 deletions(-) + +diff --git a/test/boothrunner.py b/test/boothrunner.py +index 347912b..31c2213 100644 +--- a/test/boothrunner.py ++++ b/test/boothrunner.py +@@ -1,8 +1,6 @@ +-import os + import sys + import subprocess + import time +-import unittest + + class BoothRunner: + default_config_file = '/etc/booth/booth.conf' +diff --git a/test/boothtestenv.py b/test/boothtestenv.py +index 59e25c3..ba54360 100644 +--- a/test/boothtestenv.py ++++ b/test/boothtestenv.py +@@ -5,7 +5,6 @@ import tempfile + import unittest + + from assertions import BoothAssertions +-from boothrunner import BoothRunner + + class BoothTestEnvironment(unittest.TestCase, BoothAssertions): + test_src_path = os.path.abspath(os.path.dirname(__file__)) +diff --git a/test/runtests.py.in b/test/runtests.py.in +index ec59159..73d70a3 100644 +--- a/test/runtests.py.in ++++ b/test/runtests.py.in +@@ -1,7 +1,6 @@ + #!@PYTHON_SHEBANG@ + + import os +-import re + import shutil + import sys + import tempfile +@@ -10,7 +9,7 @@ import unittest + + from clienttests import ClientConfigTests + from sitetests import SiteConfigTests +-from arbtests import ArbitratorConfigTests ++#from arbtests import ArbitratorConfigTests + + if __name__ == '__main__': + if os.geteuid() == 0: +diff --git a/test/serverenv.py b/test/serverenv.py +index 5d6c6c4..7b8915d 100644 +--- a/test/serverenv.py ++++ b/test/serverenv.py +@@ -1,9 +1,7 @@ + import os + import re + import time +-import unittest + +-from assertions import BoothAssertions + from boothrunner import BoothRunner + from boothtestenv import BoothTestEnvironment + from utils import get_IP +diff --git a/test/servertests.py b/test/servertests.py +index 288d19f..f72dbed 100644 +--- a/test/servertests.py ++++ b/test/servertests.py +@@ -1,5 +1,4 @@ + import copy +-from pprint import pprint, pformat + import re + import string + +-- +2.18.0.rc2 + diff --git a/SOURCES/0006-test-avoid-dangerous-mutable-sticky-default-value.patch b/SOURCES/0006-test-avoid-dangerous-mutable-sticky-default-value.patch new file mode 100644 index 0000000..88ba09c --- /dev/null +++ b/SOURCES/0006-test-avoid-dangerous-mutable-sticky-default-value.patch @@ -0,0 +1,178 @@ +From 34cc2fcda6804d42ee66fa5a417fc42b64fe3806 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Tue, 10 Jul 2018 19:45:56 +0200 +Subject: [PATCH] test: avoid dangerous mutable/sticky default value +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Incl. slight refactoring towards more frequent use of tuples where +advantage of lists are dubious. + +Signed-off-by: Jan Pokorný +--- + script/unit-test.py.in | 34 ++++++++++++++++------------------ + test/assertions.py | 2 +- + test/boothrunner.py | 14 +++++++------- + test/clientenv.py | 4 ++-- + test/serverenv.py | 4 ++-- + 5 files changed, 28 insertions(+), 30 deletions(-) + +diff --git a/script/unit-test.py.in b/script/unit-test.py.in +index 4f3cf62..fc98bc1 100644 +--- a/script/unit-test.py.in ++++ b/script/unit-test.py.in +@@ -199,7 +199,7 @@ class UT(): + self.booth.close( force=self.booth.isalive() ) + + +- def start_a_process(self, bin, env_add=[], **args): ++ def start_a_process(self, bin, env_add=(), **args): + name = re.sub(r".*/", "", bin) + # How to get stderr, too? + expct = pexpect.spawn(bin, +@@ -220,16 +220,15 @@ class UT(): + + def start_processes(self, test): + self.booth = self.start_a_process(self.binary, +- args = [ "daemon", "-D", +- "-c", self.test_base + "/booth.conf", +- "-s", "127.0.0.1", +- "-l", self.lockfile, +- ], +- env_add=[ ('UNIT_TEST', test), ++ args = ["daemon", "-D", ++ "-c", self.test_base + "/booth.conf", ++ "-s", "127.0.0.1", ++ "-l", self.lockfile], ++ env_add=( ('UNIT_TEST', test), + ('UNIT_TEST_FILE', os.path.realpath(test)), + # provide some space, so that strcpy(getenv()) works + ('UNIT_TEST_AUX', "".zfill(1024)), +- ]); ++ )); + + logging.info("started booth with PID %d, lockfile %s" % (self.booth.pid, self.lockfile)) + self.booth.expect("BOOTH site \S+ \(build \S+\) daemon is starting", timeout=2) +@@ -237,16 +236,15 @@ class UT(): + + self.gdb = self.start_a_process("gdb", + args=["-quiet", +- "-p", str(self.booth.pid), +- # Don't use .gdbinit +- "-nx", "-nh", +- # Run until the defined point. +- # This is necessary so that ticket state setting doesn't +- # happen _before_ the call to pcmk_load_ticket() +- # (which would overwrite our data) +- "-ex", "break ticket_cron", +- "-ex", "continue", +- ]) ++ "-p", str(self.booth.pid), ++ # Don't use .gdbinit ++ "-nx", "-nh", ++ # Run until the defined point. ++ # This is necessary so that ticket state setting doesn't ++ # happen _before_ the call to pcmk_load_ticket() ++ # (which would overwrite our data) ++ "-ex", "break ticket_cron", ++ "-ex", "continue"]) + logging.info("started GDB with PID %d" % self.gdb.pid) + self.gdb.expect("(gdb)") + self.gdb.sendline("set pagination off\n") +diff --git a/test/assertions.py b/test/assertions.py +index fafb291..db6fcd8 100644 +--- a/test/assertions.py ++++ b/test/assertions.py +@@ -10,7 +10,7 @@ class BoothAssertions: + self.assertRegexpMatches(stderr, expected_error) + + def assertLockFileError(self, config_file=None, config_text=None, +- lock_file=True, args=[]): ++ lock_file=True, args=()): + (pid, ret, stdout, stderr, runner) = \ + self.run_booth(config_text=config_text, config_file=config_file, + lock_file=lock_file, args=args, expected_exitcode=1) +diff --git a/test/boothrunner.py b/test/boothrunner.py +index 31c2213..0285fe6 100644 +--- a/test/boothrunner.py ++++ b/test/boothrunner.py +@@ -8,14 +8,14 @@ class BoothRunner: + + def __init__(self, boothd_path, mode, args): + self.boothd_path = boothd_path +- self.args = [ mode ] +- self.final_args = args # will be appended to self.args ++ self.args = (mode, ) ++ self.final_args = tuple(args) # will be appended to self.args + self.mode = mode + self.config_file = None + self.lock_file = None + + def set_config_file_arg(self): +- self.args += [ '-c', self.config_file ] ++ self.args += ('-c', self.config_file) + + def set_config_file(self, config_file): + self.config_file = config_file +@@ -23,16 +23,16 @@ class BoothRunner: + + def set_lock_file(self, lock_file): + self.lock_file = lock_file +- self.args += [ '-l', self.lock_file ] ++ self.args += ('-l', self.lock_file) + + def set_debug(self): +- self.args += [ '-D' ] ++ self.args += ('-D', ) + + def set_foreground(self): +- self.args += [ '-S' ] ++ self.args += ('-S', ) + + def all_args(self): +- return [ self.boothd_path ] + self.args + self.final_args ++ return (self.boothd_path, ) + self.args + self.final_args + + def show_output(self, stdout, stderr): + if stdout: +diff --git a/test/clientenv.py b/test/clientenv.py +index 73b2791..141e33c 100644 +--- a/test/clientenv.py ++++ b/test/clientenv.py +@@ -4,8 +4,8 @@ from boothrunner import BoothRunner + class ClientTestEnvironment(BoothTestEnvironment): + mode = 'client' + +- def run_booth(self, config_text=None, config_file=None, lock_file=True, args=[], +- expected_exitcode=0, debug=False): ++ def run_booth(self, config_text=None, config_file=None, lock_file=True, ++ args=(), expected_exitcode=0, debug=False): + ''' + Runs boothd. + +diff --git a/test/serverenv.py b/test/serverenv.py +index 7b8915d..62c37d0 100644 +--- a/test/serverenv.py ++++ b/test/serverenv.py +@@ -29,7 +29,7 @@ ticket="ticketB" + + def run_booth(self, expected_exitcode, expected_daemon, + config_text=None, config_file=None, lock_file=True, +- args=[], debug=False, foreground=False): ++ args=(), debug=False, foreground=False): + ''' + Runs boothd. Defaults to using a temporary lock file and the + standard config file path. There are four possible types of +@@ -52,7 +52,7 @@ ticket="ticketB" + True: pass a temporary lockfile parameter to booth via -l + string: pass the given lockfile path to booth via -l + args +- array of extra args to pass to booth ++ iterable of extra args to pass to booth + expected_exitcode + an integer, or False if booth is not expected to terminate + within the timeout +-- +2.18.0.rc2 + diff --git a/SOURCES/0007-test-unit-test.py-daemon-will-not-stay-in-foreground.patch b/SOURCES/0007-test-unit-test.py-daemon-will-not-stay-in-foreground.patch new file mode 100644 index 0000000..83058df --- /dev/null +++ b/SOURCES/0007-test-unit-test.py-daemon-will-not-stay-in-foreground.patch @@ -0,0 +1,41 @@ +From 0a7b51d1eb6f948724c08e94148e8ff1f448d100 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Fri, 13 Jul 2018 14:10:28 +0200 +Subject: [PATCH] test: unit-test.py: daemon will not stay in foreground with + -D anymore +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit a66ac33 missed this impact (it may have missed impact on +test/boothrunner.py but it appears to be fine either way). + +Signed-off-by: Jan Pokorný +--- + script/unit-test.py.in | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/script/unit-test.py.in b/script/unit-test.py.in +index fc98bc1..74a014b 100644 +--- a/script/unit-test.py.in ++++ b/script/unit-test.py.in +@@ -2,7 +2,6 @@ + # vim: fileencoding=utf-8 + # see http://stackoverflow.com/questions/728891/correct-way-to-define-python-source-code-encoding + # NOTE: setting the encoding is needed as non-ASCII characters are contained +-# FIXME: apparently, pexpect.EOF is not being excepted properly + + import os, sys, time, signal, tempfile, socket, posix, time + import re, shutil, pexpect, logging, pprint +@@ -220,7 +219,7 @@ class UT(): + + def start_processes(self, test): + self.booth = self.start_a_process(self.binary, +- args = ["daemon", "-D", ++ args = ["daemon", "-DS", + "-c", self.test_base + "/booth.conf", + "-s", "127.0.0.1", + "-l", self.lockfile], +-- +2.18.0.rc2 + diff --git a/SOURCES/0008-Refactor-fix-strncpy-may-miss-trailing-null-byte-war.patch b/SOURCES/0008-Refactor-fix-strncpy-may-miss-trailing-null-byte-war.patch new file mode 100644 index 0000000..bbb56f1 --- /dev/null +++ b/SOURCES/0008-Refactor-fix-strncpy-may-miss-trailing-null-byte-war.patch @@ -0,0 +1,50 @@ +From d3bf9f5ced41ad0f4e8ae87e80c7e44df4157b61 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Fri, 13 Jul 2018 14:40:07 +0200 +Subject: [PATCH] Refactor: fix "strncpy may miss trailing null byte" warnings + of GCC 8.1 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Verbatim warning: +> ‘strncpy’ specified bound 64 equals destination size [-Wstringop-truncation] + +Signed-off-by: Jan Pokorný +--- + src/config.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/src/config.c b/src/config.c +index 9df5767..e4d36ab 100644 +--- a/src/config.c ++++ b/src/config.c +@@ -75,7 +75,10 @@ static void hostname_to_ip(char * hostname) + + /* Return the first found address */ + if (addr_list[0] != NULL) { +- strncpy(hostname, inet_ntoa(*addr_list[0]), BOOTH_NAME_LEN); ++ strncpy(hostname, inet_ntoa(*addr_list[0]), BOOTH_NAME_LEN - 1); ++ /* buffer overflow will not happen (IPv6 notation < 63 chars), ++ but suppress the warnings */ ++ hostname[BOOTH_NAME_LEN - 1] = '\0'; + } + else { + log_error("no IP addresses found for the host \"%s\"", hostname); +@@ -106,7 +109,12 @@ static int add_site(char *addr_string, int type) + site->family = AF_INET; + site->type = type; + +- strncpy(site->addr_string, addr_string, sizeof(site->addr_string)); ++ /* buffer overflow will not hapen (we've already checked that ++ addr_string will fit incl. terminating '\0' above), but ++ suppress the warnings with copying everything but the boundary ++ byte, which is valid as-is, since this last byte will be safely ++ pre-zeroed from the struct booth_config initialization */ ++ strncpy(site->addr_string, addr_string, sizeof(site->addr_string) - 1); + + if (!(inet_pton(AF_INET, site->addr_string, &site->sa4.sin_addr) > 0) && + !(inet_pton(AF_INET6, site->addr_string, &site->sa6.sin6_addr) > 0)) { +-- +2.18.0.rc2 + diff --git a/SOURCES/0009-maint-lsb-polish-arbitrator-s-initscript-fix-condres.patch b/SOURCES/0009-maint-lsb-polish-arbitrator-s-initscript-fix-condres.patch new file mode 100644 index 0000000..ef8d9c4 --- /dev/null +++ b/SOURCES/0009-maint-lsb-polish-arbitrator-s-initscript-fix-condres.patch @@ -0,0 +1,145 @@ +From 5703061ba7142509647eb6ee8a0155464677b45a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Thu, 19 Jul 2018 18:45:59 +0200 +Subject: [PATCH 1/3] maint: lsb: polish arbitrator's initscript, fix + condrestart +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +- previously, condrestart/try-restart (regardless whether "multi-tenant" + or not) would not work as expected +- make it apparent the script is in fact not fully LSB-compliant +- suppress some ShellCheck issues, mostly related to the fact that + some variables are injected based on "boothd status" invocation, + also drop an unused variable (see commits 8732542 + 054dafa + [until which it might have been useful] + 9e6359f) +- fix whitespace issues + normalize indentation of "case - esac" + +Signed-off-by: Jan Pokorný +--- + script/lsb/booth-arbitrator | 64 ++++++++++++++++++++++--------------- + 1 file changed, 38 insertions(+), 26 deletions(-) + +diff --git a/script/lsb/booth-arbitrator b/script/lsb/booth-arbitrator +index d90fe4b..3cdafc0 100755 +--- a/script/lsb/booth-arbitrator ++++ b/script/lsb/booth-arbitrator +@@ -1,6 +1,7 @@ + #!/bin/bash + # +-# BOOTH daemon init script for LSB-compliant Linux distributions. ++# BOOTH daemon init script for SUSE Linux based distributions ++# (almost LSB-compliant, except for s/startproc/start_daemon/ etc.) + # + # booth-arbitrator BOOTH arbitrator daemon + # +@@ -40,12 +41,13 @@ check_status() { + + rc=$BOOTH_ERROR_GENERIC + eval `"$exec" status "${cnf:+-c$cnf}" ; echo rc=$?` +- case $rc in ++ case $rc in + 0) +- case "$booth_state" in ++ # shellcheck disable=SC2154 ++ case "$booth_state" in + started) return $BOOTH_DAEMON_STARTED;; + starting) return $BOOTH_DAEMON_STARTING;; +- *) return $BOOTH_ERROR_GENERIC;; ++ *) return $BOOTH_ERROR_GENERIC;; + esac + ;; + $OCF_NOT_RUNNING) return $BOOTH_DAEMON_NOT_RUNNING;; +@@ -57,6 +59,7 @@ check_status() { + status() { + echo -n "BOOTH daemon is " + if check_status; then ++ # shellcheck disable=SC2154 + echo "running - PID $booth_lockpid for $booth_cfg_name, $booth_addr_string:$booth_port" + return 0 + else +@@ -90,16 +93,15 @@ stop() { + wait_time=5 + check_status; rc=$? + case $rc in +- $BOOTH_DAEMON_STARTED);; +- $BOOTH_DAEMON_STARTING);; +- $BOOTH_DAEMON_EXIST);; ++ $BOOTH_DAEMON_STARTED|$BOOTH_DAEMON_STARTING|$BOOTH_DAEMON_EXIST) ++ ;; + $BOOTH_DAEMON_NOT_RUNNING) + echo "BOOTH arbitrator daemon is not running." + return 0 +- ;; ++ ;; + *) return 1;; + esac +- ++ + echo -n $"Stopping BOOTH arbitrator daemon: " + # $exec stop "${cnf:+-c$cnf}" + # sleep 1 +@@ -122,11 +124,10 @@ stop() { + } + + foreach() { +- local cnf cnf_base ++ local cnf + local rc=0 + + for cnf in ${BOOTH_CONF_FILE:-$CONF_DIR/*.conf} ; do +- cnf_base=`basename $cnf` + "$@" + rc=$((rc|$?)) + done +@@ -138,20 +139,31 @@ restart() { + start + } + ++condrestart() { ++ local rc ++ ++ check_status; rc=$? ++ ++ case "$rc" in ++ $BOOTH_DAEMON_STARTED|$BOOTH_DAEMON_STARTING|$BOOTH_DAEMON_EXIST) ++ # shellcheck disable=SC2154 ++ [ ! -f "$booth_lockfile" ] || restart ++ ;; ++ esac ++} ++ + case "$1" in +- start|stop|restart) +- foreach $1 +- ;; +- reload|force-reload) +- foreach restart +- ;; +- condrestart|try-restart) +- [ ! -f "$booth_lockfile" ] || restart +- ;; +- status) +- foreach status +- ;; +- *) +- echo $"Usage: $0 {start|stop|restart|try-restart|condrestart|reload|force-reload|status}" +- exit 2 ++start|stop|restart|condrestart|status) ++ foreach $1 ++ ;; ++reload|force-reload) ++ foreach restart ++ ;; ++try-restart) ++ foreach condrestart ++ ;; ++*) ++ echo $"Usage: $0 {start|stop|restart|try-restart|condrestart|reload|force-reload|status}" ++ exit 2 ++ ;; + esac +-- +2.18.0.rc2 + diff --git a/SOURCES/0010-maint-test-polish-live_test.sh.patch b/SOURCES/0010-maint-test-polish-live_test.sh.patch new file mode 100644 index 0000000..af0dc8d --- /dev/null +++ b/SOURCES/0010-maint-test-polish-live_test.sh.patch @@ -0,0 +1,260 @@ +From 4d7278dbd9cf5f9a7d6060eb96e1ba5af6b3d505 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Thu, 19 Jul 2018 19:44:34 +0200 +Subject: [PATCH 2/3] maint: test: polish live_test.sh +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +- from issues found with ShellCheck, fix those marked as important +- suppress some ShellCheck issues, mostly related to the fact that + some ticket-related variables are established dynamically, + also drop a never-used local variable (see commits 74a3f5c + 4136682) + and another global one that was used but consequently ditched + (see commits 08f56bd + d952b27) +- fix s/boots not uptodate/booths not up-to-date/ spelling +- fix whitespace issues + +Signed-off-by: Jan Pokorný +--- + test/live_test.sh | 60 ++++++++++++++++++++++++----------------------- + 1 file changed, 31 insertions(+), 29 deletions(-) + +diff --git a/test/live_test.sh b/test/live_test.sh +index f8644a2..c131f8c 100755 +--- a/test/live_test.sh ++++ b/test/live_test.sh +@@ -41,7 +41,6 @@ EOF + [ $# -eq 0 ] && usage 0 + + cnf=$1 +-BOOTH_DIR="/etc/booth" + run_cnf="/etc/booth/booth.conf" + + shift 1 +@@ -62,9 +61,9 @@ get_internal_site() { + + logmsg() { + if [ "$WE_SERVER" -o "$_JUST_NETEM" ]; then +- logger -t "BOOTHTEST" -p $HA_LOGFACILITY.info -- $@ ++ logger -t "BOOTHTEST" -p $HA_LOGFACILITY.info -- "$@" + else +- ssh $SSH_OPTS `get_site 1` logger -t "BOOTHTEST" -p $HA_LOGFACILITY.info -- $@ ++ ssh $SSH_OPTS `get_site 1` logger -t "BOOTHTEST" -p $HA_LOGFACILITY.info -- "$@" + fi + } + +@@ -141,20 +140,20 @@ local_netem_env() { + } + + is_function() { +- test z"`command -v $1`" = z"$1" ++ test z"`command -v $1`" = z"$1" + } + runcmd() { + local h=$1 rc + shift 1 +- echo "$h: running '$@'" | logmsg ++ echo "$h: running '$*'" | logmsg + if ip a l | fgrep -wq $h; then +- eval $@ ++ eval "$@" + else +- ssh $SSH_OPTS $h $@ ++ ssh $SSH_OPTS $h "$@" + fi + rc=$? + if [ $rc -ne 0 ]; then +- echo "$h: '$@' failed (exit code $rc)" | logmsg ++ echo "$h: '$*' failed (exit code $rc)" | logmsg + fi + return $rc + } +@@ -306,7 +305,7 @@ dump_conf() { + forall() { + local h rc=0 + for h in $sites $arbitrators; do +- runcmd $h $@ ++ runcmd $h "$@" + rc=$((rc|$?)) + done + return $rc +@@ -314,7 +313,7 @@ forall() { + forall_withname() { + local h rc=0 output + for h in $sites $arbitrators; do +- output=`runcmd $h $@` ++ output=`runcmd $h "$@"` + rc=$((rc|$?)) + echo $h: $output + done +@@ -323,7 +322,7 @@ forall_withname() { + forall_sites() { + local h rc=0 + for h in $sites; do +- runcmd $h $@ ++ runcmd $h "$@" + rc=$((rc|$?)) + done + return $rc +@@ -343,7 +342,7 @@ forall_fun2() { + f=$1 + shift 1 + for h in $sites $arbitrators; do +- $f $@ | ssh $SSH_OPTS $h ++ $f "$@" | ssh $SSH_OPTS $h + rc=$((rc|$?)) + [ $rc -ne 0 ] && break + done +@@ -353,13 +352,13 @@ run_site() { + local n=$1 h + shift 1 + h=`echo $sites | awk '{print $'$n'}'` +- runcmd $h $@ ++ runcmd $h "$@" + } + run_arbitrator() { + local n=$1 h + shift 1 + h=`echo $arbitrators | awk '{print $'$n'}'` +- runcmd $h $@ ++ runcmd $h "$@" + } + + # need to get logs from _all_ clusters' nodes +@@ -450,6 +449,8 @@ n && (/^$/ || /^ticket.*/) {exit} + ' $1 + } + wait_exp() { ++ # shellcheck disable=SC2154 ++ # (T_expire: defined with get_tkt_settings) + sleep $T_expire + } + wait_renewal() { +@@ -593,7 +594,7 @@ booth_leader_consistency_2() { + # b) some booths not uptodate (have no leader for the ticket) + # c) ticket expiry times differ + check_booth_consistency() { +- local tlist tlist_validate rc rc_lead maxdiff ++ local tlist rc rc_lead maxdiff + tlist=`forall_withname booth list 2>/dev/null | grep $tkt` + + # Check time consistency +@@ -620,7 +621,7 @@ check_booth_consistency() { + `if [ $rc -ge 4 ]; then + echo "booth list consistency failed (more than one leader!):" + elif [ $rc -ge 2 ]; then +- echo "booth list consistency failed (some boots not uptodate):" ++ echo "booth list consistency failed (some booths not up-to-date):" + else + echo "booth list consistency failed (max valid time diff: $maxdiff):" + fi` +@@ -677,11 +678,10 @@ run_report() { + runtest() { + local start_ts end_ts + local rc booth_status dep_rsc_status +- local start_time end_time + local usrmsg + rc=0 + TEST=$1 +- start_time=`date` ++ start_ts=`date` # to have the expanded form in the logfile + start_ts=`date +%s` + echo -n "Testing: $1 (ticket: $tkt)... " + can_run_test $1 || return 0 +@@ -719,7 +719,7 @@ runtest() { + usrmsg="test FAIL: $rc" + ;; + esac +- end_time=`date` ++ end_ts=`date` # to have the expanded form in the logfile + end_ts=`date +%s` + echo "finished booth test $1 ($tkt): $usrmsg" | logmsg + echo "==================================================" | logmsg +@@ -791,6 +791,8 @@ setup_longgrant2() { + } + test_longgrant2() { + local i ++ # shellcheck disable=SC2034 ++ # (variable exists merely out of necessity) + for i in `seq 10`; do + wait_exp + done +@@ -1033,14 +1035,14 @@ setup_split_leader() { + return 0 + } + test_split_leader() { +- run_site 1 $iprules stop $port >/dev/null ++ run_site 1 $iprules stop $port >/dev/null + wait_exp + wait_timeout + wait_timeout + wait_timeout + wait_timeout + check_cib any || return 1 +- run_site 1 $iprules start $port >/dev/null ++ run_site 1 $iprules start $port >/dev/null + wait_timeout + wait_timeout + wait_timeout +@@ -1049,7 +1051,7 @@ check_split_leader() { + check_consistency any + } + recover_split_leader() { +- run_site 1 $iprules start $port >/dev/null ++ run_site 1 $iprules start $port >/dev/null + } + + ## TEST: split_follower ## +@@ -1059,10 +1061,10 @@ setup_split_follower() { + grant_ticket_cib 1 + } + test_split_follower() { +- run_site 2 $iprules stop $port >/dev/null ++ run_site 2 $iprules stop $port >/dev/null + wait_exp + wait_timeout +- run_site 2 $iprules start $port >/dev/null ++ run_site 2 $iprules start $port >/dev/null + wait_timeout + } + check_split_follower() { +@@ -1076,9 +1078,9 @@ setup_split_edge() { + grant_ticket_cib 1 + } + test_split_edge() { +- run_site 1 $iprules stop $port >/dev/null ++ run_site 1 $iprules stop $port >/dev/null + wait_exp +- run_site 1 $iprules start $port >/dev/null ++ run_site 1 $iprules start $port >/dev/null + wait_timeout + wait_timeout + } +@@ -1215,13 +1217,11 @@ internal_arbitrators=`get_value arbitrator < $cnf` + all_nodes=`get_all_nodes` + port=`get_value port < $cnf` + : ${port:=9929} +-site_cnt=`echo $internal_sites | wc -w` +-arbitrator_cnt=`echo $internal_arbitrators | wc -w` + + if [ "$1" = "__netem__" ]; then + shift 1 + _JUST_NETEM=1 +- local_netem_env $@ ++ local_netem_env "$@" + exit + fi + +@@ -1314,6 +1314,8 @@ do + + eval `get_tkt_settings booth_${i}.conf` + ++ # shellcheck disable=SC2154 ++ # (T_timeout: defined with get_tkt_settings) + MIN_TIMEOUT=`awk -v tm=$T_timeout 'BEGIN{ + if (tm >= 2) print tm; + else print 2*tm; +-- +2.18.0.rc2 + diff --git a/SOURCES/0011-maint-ocf-script-eliminate-some-false-positives-with.patch b/SOURCES/0011-maint-ocf-script-eliminate-some-false-positives-with.patch new file mode 100644 index 0000000..0aeb51a --- /dev/null +++ b/SOURCES/0011-maint-ocf-script-eliminate-some-false-positives-with.patch @@ -0,0 +1,120 @@ +From 6eb95429bb92cab5616feaef0111733f79164811 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= +Date: Thu, 19 Jul 2018 19:11:36 +0200 +Subject: [PATCH 3/3] maint: ocf + script: eliminate some false positives with + ShellCheck +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In particular, prevent sed's character classes to be confused with +array-like variable access through specified index, which demonstrates +why it's better to have the ${variable} "enbraced". + +Signed-off-by: Jan Pokorný +--- + script/ocf/booth-site | 7 +++++++ + script/ocf/geostore | 2 ++ + script/ocf/sharedrsc | 5 +++++ + script/service-runnable.in | 2 +- + 4 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/script/ocf/booth-site b/script/ocf/booth-site +index 809928c..8178e35 100755 +--- a/script/ocf/booth-site ++++ b/script/ocf/booth-site +@@ -30,6 +30,7 @@ DEFAULT_BIN="boothd" + DEFAULT_CONF="/etc/booth/booth.conf" + + : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} ++# shellcheck source=/usr/lib/ocf/lib/heartbeat/ocf-shellfuncs + . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + + ####################################################################### +@@ -140,6 +141,8 @@ booth_site_start() { + $OCF_NOT_RUNNING) ;; + esac + ++ # shellcheck disable=SC2154 ++ # (OCF_RESKEY_args: injected by CRM) + $OCF_RESKEY_daemon daemon -c $OCF_RESKEY_config $OCF_RESKEY_args || + return $OCF_ERR_GENERIC + sleep 1 +@@ -188,6 +191,8 @@ booth_site_validate_all() { + return $OCF_ERR_INSTALLED + fi + ++ # shellcheck disable=SC2154 ++ # (OCF_RESKEY_CRM_meta_globally_unique: injected by CRM) + if ocf_is_true $OCF_RESKEY_CRM_meta_globally_unique; then + ocf_log err "$OCF_RESOURCE_INSTANCE must be configured with the globally_unique=false meta attribute" + return $OCF_ERR_CONFIGURED +@@ -198,6 +203,8 @@ booth_site_validate_all() { + + : ${OCF_RESKEY_daemon:=$DEFAULT_BIN} + : ${OCF_RESKEY_config:=$DEFAULT_CONF} ++# shellcheck disable=SC2034 ++# (OCF_REQUIRED_BINARIES consumed by ocf_rarun) + OCF_REQUIRED_BINARIES=${OCF_RESKEY_daemon} + + ocf_rarun $* +diff --git a/script/ocf/geostore b/script/ocf/geostore +index 85842a8..c180418 100755 +--- a/script/ocf/geostore ++++ b/script/ocf/geostore +@@ -31,7 +31,9 @@ + # Initialization: + + : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} ++# shellcheck source=/usr/lib/ocf/lib/heartbeat/ocf-shellfuncs + . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs ++# shellcheck source=script/ocf/geo_attr.sh + . ${OCF_ROOT}/lib/booth/geo_attr.sh + + ####################################################################### +diff --git a/script/ocf/sharedrsc b/script/ocf/sharedrsc +index 384cfd2..c2ed8ff 100755 +--- a/script/ocf/sharedrsc ++++ b/script/ocf/sharedrsc +@@ -36,6 +36,7 @@ + # Initialization: + + : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} ++# shellcheck source=/usr/lib/ocf/lib/heartbeat/ocf-shellfuncs + . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + + ####################################################################### +@@ -148,6 +149,8 @@ sharedrsc_start() { + if ! owner=`runcmd getowner $DIR`; then + owner="... nobody, it's only half-claimed" + fi ++ # shellcheck disable=SC2154 ++ # (OCF_RESKEY_dir: injected by CRM) + ocf_log err "eek, $OCF_RESKEY_dir already owned by $owner" + return $OCF_ERR_GENERIC + } +@@ -180,6 +183,8 @@ sharedrsc_validate_all() { + return $OCF_SUCCESS + } + ++# shellcheck disable=SC2034 ++# (OCF_REQUIRED_PARAMS consumed by ocf_rarun) + OCF_REQUIRED_PARAMS="dir" + ocf_rarun $* + +diff --git a/script/service-runnable.in b/script/service-runnable.in +index 9ea33d4..2f58641 100755 +--- a/script/service-runnable.in ++++ b/script/service-runnable.in +@@ -30,7 +30,7 @@ fi + + if echo "$status" | + sed -n '/^Revised cluster status:/,$p' | +- egrep "^[[:space:]]+$service[[:space:]]+\(.*\):[[:space:]]+Started ([^[:space:]]+) *$" >/dev/null ++ egrep "^[[:space:]]+${service}[[:space:]]+\(.*\):[[:space:]]+Started ([^[:space:]]+) *$" >/dev/null + then + # can be started - we're done. + exit 0 +-- +2.18.0.rc2 + diff --git a/SPECS/booth.spec b/SPECS/booth.spec new file mode 100644 index 0000000..6a3e62c --- /dev/null +++ b/SPECS/booth.spec @@ -0,0 +1,325 @@ +# RPMs are split as follows: +# * booth: +# - envelope package serving as a syntactic shortcut to install +# booth-site (with architecture reliably preserved) +# * booth-core: +# - package serving as a base for booth-{arbitrator,site}, +# carrying also basic documentation, license, etc. +# * booth-arbitrator: +# - package to be installed at a machine accessible within HA cluster(s), +# but not (necessarily) a member of any, hence no dependency +# on anything from cluster stack is required +# * booth-site: +# - package to be installed at a cluster member node +# (requires working cluster environment to be useful) +# * booth-test: +# - files for testing booth +# +# TODO: +# wireshark-dissector.lua currently of no use (rhbz#1259623), but if/when +# this no longer persists, add -wireshark package (akin to libvirt-wireshark) + +%bcond_with html_man +%bcond_with glue + +# Disable automatic compilation of Python files in extra directories +%global _python_bytecompile_extra 0 + +%global specver 5 +%global boothver 1.0 +# set following to the actual commit or, for final release, concatenate +# "boothver" macro to "v" (will yield a tag per the convention) +%global commit f2d38ce3d61502bda2a28e79db103737a691faf4 +%global lparen ( +%global rparen ) +%global shortcommit %(c=%{commit}; case ${c} in + v*%{rparen} echo ${c:1};; + *%{rparen} echo ${c:0:7};; esac) +%global pre_release %(s=%{shortcommit}; [ ${s: -3:2} != rc ]; echo $?) +%global post_release %([ %{commit} = v%{shortcommit} ]; echo $?) +%global github_owner ClusterLabs + +%if 0%{pre_release} +%global boothrel 0.%{specver}.%(s=%{shortcommit}; echo ${s: -3}) +%else +%if 0%{post_release} +%global boothrel %{specver}.%{shortcommit}.git +%else +%global boothrel %{specver} +%endif +%endif + +%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}} +# https://fedoraproject.org/wiki/EPEL:Packaging?rd=Packaging:EPEL#The_.25license_tag +%{!?_licensedir:%global license %doc} + +%global test_path %{_datadir}/booth/tests + +Name: booth +Version: %{boothver} +Release: %{boothrel}%{dist} +Summary: Ticket Manager for Multi-site Clusters +Group: System Environment/Daemons +License: GPLv2+ +Url: https://github.com/%{github_owner}/%{name} +Source0: https://github.com/%{github_owner}/%{name}/archive/%{commit}/%{name}-%{shortcommit}.tar.gz +Patch0: 0000-test-remove-superfluous-shebangs-for-import-only-mod.patch +Patch1: 0001-test-do-not-mix-tabs-with-spaces-in-Python-code.patch +Patch2: 0002-test-make-Python-files-supported-_also_-with-Python-.patch +Patch3: 0003-build-parametrize-Python-invocations-in-the-shebangs.patch +Patch4: 0004-test-drop-underqualified-identifier-in-unittest-s-2..patch +Patch5: 0005-test-drop-comment-out-superfluous-imports.patch +Patch6: 0006-test-avoid-dangerous-mutable-sticky-default-value.patch +Patch7: 0007-test-unit-test.py-daemon-will-not-stay-in-foreground.patch +Patch8: 0008-Refactor-fix-strncpy-may-miss-trailing-null-byte-war.patch +Patch9: 0009-maint-lsb-polish-arbitrator-s-initscript-fix-condres.patch +Patch10: 0010-maint-test-polish-live_test.sh.patch +Patch11: 0011-maint-ocf-script-eliminate-some-false-positives-with.patch + +# direct build process dependencies +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: coreutils +BuildRequires: make +## ./autogen.sh +BuildRequires: /bin/sh +# general build dependencies +BuildRequires: asciidoc +BuildRequires: gcc +BuildRequires: pkgconfig +# linking dependencies +BuildRequires: libgcrypt-devel +BuildRequires: libxml2-devel +## just for include +BuildRequires: pacemaker-libs-devel +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: zlib-devel +## logging provider +BuildRequires: pkgconfig(libqb) +## random2range provider +BuildRequires: pkgconfig(glib-2.0) +## nametag provider +BuildRequires: pkgconfig(libsystemd) +# check scriptlet (for hostname and killall respectively) +BuildRequires: hostname psmisc +BuildRequires: python3-devel +# spec file specifics +## for _unitdir, systemd_requires and specific scriptlet macros +BuildRequires: systemd +## for autosetup +BuildRequires: git + +# this is for a composite-requiring-its-components arranged +# as an empty package (empty files section) requiring subpackages +# (_isa so as to preserve the architecture) +Requires: %{name}-core%{?_isa} +Requires: %{name}-site +%files +# intentionally empty + +%description +Booth manages tickets which authorize cluster sites located +in geographically dispersed locations to run resources. +It facilitates support of geographically distributed +clustering in Pacemaker. + +# SUBPACKAGES # + +%package core +Summary: Booth core files (executables, etc.) +Group: System Environment/Daemons +# for booth-keygen (chown, dd) +Requires: coreutils +# deal with pre-split arrangement +Conflicts: %{name} < 1.0-1 + +%description core +Core files (executables, etc.) for Booth, ticket manager for +multi-site clusters. + +%package arbitrator +Summary: Booth support for running as an arbitrator +Group: System Environment/Daemons +BuildArch: noarch +Requires: %{name}-core = %{version}-%{release} +%{?systemd_requires} +# deal with pre-split arrangement +Conflicts: %{name} < 1.0-1 + +%description arbitrator +Support for running Booth, ticket manager for multi-site clusters, +as an arbitrator. + +%post arbitrator +%systemd_post booth@.service booth-arbitrator.service + +%preun arbitrator +%systemd_preun booth@.service booth-arbitrator.service + +%postun arbitrator +%systemd_postun_with_restart booth@.service booth-arbitrator.service + +%package site +Summary: Booth support for running as a full-fledged site +Group: System Environment/Daemons +BuildArch: noarch +Requires: %{name}-core = %{version}-%{release} +# for crm_{resource,simulate,ticket} utilities +Requires: pacemaker >= 1.1.8 +# for ocf-shellfuncs and other parts of OCF shell-based environment +Requires: resource-agents +# deal with pre-split arrangement +Conflicts: %{name} < 1.0-1 + +%description site +Support for running Booth, ticket manager for multi-site clusters, +as a full-fledged site. + +%package test +Summary: Test scripts for Booth +Group: System Environment/Daemons +BuildArch: noarch +# runtests.py suite (for hostname and killall respectively) +Requires: hostname psmisc +# any of the following internal dependencies will pull -core package +## for booth@booth.service +Requires: %{name}-arbitrator = %{version}-%{release} +## for booth-site and service-runnable scripts +## (and /usr/lib/ocf/resource.d/booth) +Requires: %{name}-site = %{version}-%{release} +Requires: gdb +Requires: %{__python3} +Requires: python3-pexpect + +%description test +Automated tests for running Booth, ticket manager for multi-site clusters. + +# BUILD # + +%prep +%autosetup -n %{name}-%{commit} -S git_am + +%build +./autogen.sh +%{configure} \ + --with-initddir=%{_initrddir} \ + --docdir=%{_pkgdocdir} \ + --enable-user-flags \ + %{!?with_html_man:--without-html_man} \ + %{!?with_glue:--without-glue} +%{make_build} + +%install +%{make_install} +mkdir -p %{buildroot}/%{_unitdir} +cp -a -t %{buildroot}/%{_unitdir} \ + -- conf/booth@.service conf/booth-arbitrator.service +install -D -m 644 -t %{buildroot}/%{_mandir}/man8 \ + -- docs/boothd.8 +ln -s boothd.8 %{buildroot}/%{_mandir}/man8/booth.8 +cp -a -t %{buildroot}/%{_pkgdocdir} \ + -- ChangeLog README-testing conf/booth.conf.example +# drop what we don't package anyway (COPYING added via tarball-relative path) +rm -rf %{buildroot}/%{_initrddir}/booth-arbitrator +rm -rf %{buildroot}/%{_pkgdocdir}/README.upgrade-from-v0.1 +rm -rf %{buildroot}/%{_pkgdocdir}/COPYING +# tests +mkdir -p %{buildroot}/%{test_path} +cp -a -t %{buildroot}/%{test_path} \ + -- conf test unit-tests script/unit-test.py +chmod +x %{buildroot}/%{test_path}/test/booth_path +chmod +x %{buildroot}/%{test_path}/test/live_test.sh +mkdir -p %{buildroot}/%{test_path}/src +ln -s -t %{buildroot}/%{test_path}/src \ + -- %{_sbindir}/boothd + +# https://fedoraproject.org/wiki/Packaging:Python_Appendix#Manual_byte_compilation +%py_byte_compile %{__python3} %{buildroot}/%{test_path} + +%check +# alternatively: test/runtests.py +VERBOSE=1 make check + +%files core +%license COPYING +%doc %{_pkgdocdir}/AUTHORS +%doc %{_pkgdocdir}/ChangeLog +%doc %{_pkgdocdir}/README +%doc %{_pkgdocdir}/booth.conf.example +# core command(s) + man pages +%{_sbindir}/booth* +%{_mandir}/man8/booth*.8* +# configuration +%dir %{_sysconfdir}/booth +%exclude %{_sysconfdir}/booth/booth.conf.example + +%files arbitrator +%{_unitdir}/booth@.service +%{_unitdir}/booth-arbitrator.service + +%files site +# OCF (agent + a helper) +## /usr/lib/ocf/resource.d/pacemaker provided by pacemaker +/usr/lib/ocf/resource.d/pacemaker/booth-site +%dir /usr/lib/ocf/lib/booth + /usr/lib/ocf/lib/booth/geo_attr.sh +# geostore (command + OCF agent) +%{_sbindir}/geostore +%{_mandir}/man8/geostore.8* +## /usr/lib/ocf/resource.d provided by resource-agents +%dir /usr/lib/ocf/resource.d/booth + /usr/lib/ocf/resource.d/booth/geostore +# helper (possibly used in the configuration hook) +%dir %{_datadir}/booth + %{_datadir}/booth/service-runnable + +%files test +%doc %{_pkgdocdir}/README-testing +# /usr/share/booth provided by -site +%{test_path} +# /usr/lib/ocf/resource.d/booth provided by -site +/usr/lib/ocf/resource.d/booth/sharedrsc + +%changelog +* Wed Sep 19 2018 Tomas Orsava - 1.0-5.f2d38ce.git +- Require the Python interpreter directly instead of using the package name +- Related: rhbz#1619153 + +* Thu Jul 19 2018 Jan Pokorný - 1.0-4.f2d38ce.git +- revert back to using asciidoc instead of asciidoctor for generating man pages + (rhbz#1603119) +- fix some issues in the shell scripts (rhbz#1602455) + +* Mon Jul 16 2018 Jan Pokorný - 1.0-3.f2d38ce.git +- update for another, current snapshot beyond booth-1.0 + (commit f2d38ce), including: + . support for solely manually managed tickets (9a365f9) + . use asciidoctor instead of asciidoc for generating man pages (65e6a6b) +- switch to using Python 3 for the tests instead of Python 2 + (behind unversioned "python" references; rhbz#1590856) + +* Thu Jun 21 2018 Troy Dawson - 1.0-2.570876d.git.3 +- Fix python shebangs (#1580601) + +* Fri Feb 10 2017 Fedora Release Engineering - 1.0-2.570876d.git.2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Tue Jul 19 2016 Fedora Release Engineering - 1.0-2.570876d.git.1 +- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages + +* Wed May 25 2016 Jan Pokorný - 1.0-3.570876d.git +- update per the changesets recently accepted by the upstream + (memory/resource leaks fixes, patches previously attached separately + that make unit test pass, internal cleanups, etc.) + +* Thu May 05 2016 Jan Pokorný - 1.0-2.eb4256a.git +- update a subset of out-of-tree patches per + https://github.com/ClusterLabs/booth/pull/22#issuecomment-216936987 +- pre-inclusion cleanups in the spec (apply systemd scriptlet operations + with booth-arbitrator, avoid overloading file implicitly considered %%doc + as %%license) + Resolves: rhbz#1314865 + Related: rhbz#1333509 + +* Thu Apr 28 2016 Jan Pokorný - 1.0-1.eb4256a.git +- initial build