def it_runs_before_debugging_a_service(self): proc = Popen(('setsid', 'pgctl', 'debug', 'sweet'), stdin=PIPE, stdout=PIPE) proc.stdin.close() try: assert proc.stdout.readline() == 'hello, i am a pre-start script in stdout\n' finally: ctrl_c(proc) proc.wait()
def run(cmd, **popen_args): """run the command, show the output, and return (stdout, stderr, returncode)""" from pgctl.subprocess import Popen, PIPE process = Popen(cmd, stdout=PIPE, stderr=PIPE, **popen_args) stdout, stderr = process.communicate() stdout, stderr = stdout.decode('UTF-8'), stderr.decode('UTF-8') show_both(stdout, stderr) return stdout, stderr, process.returncode
def it_kills_processes_holding_the_lock(self, tmpdir): lockfile = tmpdir.ensure('lock') lock = lockfile.open() process = Popen(('sleep', 'infinity')) lock.close() # assert `process` has `lock` assert list(fuser(lockfile.strpath)) == [process.pid] with mock.patch('sys.stderr', new_callable=StringIO.StringIO) as mock_stderr: terminate_runaway_processes(lockfile.strpath) assert 'WARNING: Killing these runaway ' in mock_stderr.getvalue() wait_for(lambda: process.poll() == -9)
def assert_works_interactively(): read, write = os.openpty() pty.normalize_newlines(read) # setsid: this simulates the shell's job-control behavior proc = Popen(('setsid', 'pgctl', 'debug', 'greeter'), stdin=PIPE, stdout=write) os.close(write) try: assert read_line(read) == 'What is your name?\n' proc.stdin.write(b'Buck\n') proc.stdin.flush() assert read_line(read) == 'Hello, Buck.\n' finally: ctrl_c(proc) proc.wait()
def assert_works_interactively(): read, write = os.openpty() pty.normalize_newlines(read) # setsid: this simulates the shell's job-control behavior proc = Popen(('setsid', 'pgctl-2015', 'debug', 'greeter'), stdin=PIPE, stdout=write) os.close(write) try: assert read_line(read) == 'What is your name?\n' proc.stdin.write(b'Buck\n') proc.stdin.flush() assert read_line(read) == 'Hello, Buck.\n' finally: ctrl_c(proc) proc.wait()
def it_disables_polling(): stderr = open('stderr', 'w') proc = Popen(('pgctl', 'debug', 'unreliable'), stdin=open(os.devnull), stdout=PIPE, stderr=stderr) def check_file_contents(): expected = '''\ pgctl-poll-ready: disabled during debug -- quitting ''' with open('stderr') as fd: actual = fd.read() return expected == actual wait_for(check_file_contents) proc.terminate() stderr.close()
def it_stays_locked_for_the_lifetime_of_subprocesses(self, tmpfile): from pgctl.subprocess import Popen with flock(tmpfile): p = Popen(('sleep', '99999')) assert p.poll() is None assert_locked(tmpfile) assert_locked(tmpfile) p.terminate() assert p.wait() == -15 with flock(tmpfile): print('oh hi there!')
def it_disables_polling(): stderr = open('stderr', 'w') proc = Popen(('pgctl', 'debug', 'unreliable'), stdin=open(os.devnull), stdout=PIPE, stderr=stderr) def check_file_contents(): expected = '''\ [pgctl] Stopping: unreliable [pgctl] Stopped: unreliable pgctl-poll-ready: disabled during debug -- quitting ''' with open('stderr') as fd: actual = fd.read() return expected == actual wait_for(check_file_contents) proc.terminate() stderr.close()
def it_is_disallowed(): assert_command( ('pgctl-2015', 'start'), '', '''\ [pgctl] Starting: sweet [pgctl] Started: sweet ''', 0, ) first = Popen(('pgctl-2015', 'restart'), stdout=PIPE, stderr=PIPE) # slow-shutdown takes two seconds to shut down; aim for the middle: sleep(1) second = Popen(('pgctl-2015', 'restart'), stdout=PIPE, stderr=PIPE) first_stdout, first_stderr = first.communicate() first_stdout, first_stderr = first_stdout.decode('UTF-8'), first_stderr.decode('UTF-8') show_both(first_stdout, first_stderr) assert norm.pgctl(first_stderr) == '''\ [pgctl] Stopping: sweet [pgctl] ERROR: service 'sweet' failed to stop after {TIME} seconds, its status is ready (pid {PID}) {TIME} seconds ==> playground/sweet/log <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error [pgctl] [pgctl] There might be useful information further up in the log; you can view it by running: [pgctl] less +G playground/sweet/log [pgctl] ERROR: Some services failed to stop: sweet ''' assert first_stdout == '' assert first.returncode == 1 second_stdout, second_stderr = second.communicate() second_stdout, second_stderr = second_stdout.decode('UTF-8'), second_stderr.decode('UTF-8') show_both(second_stdout, second_stderr) assert norm.pgctl(second_stderr) == '''\ [pgctl] ERROR: another pgctl command is currently managing this service: (playground/sweet/.pgctl.lock) {PS-HEADER} {PS-STATS} ${PREFIX}/bin/python ${PREFIX}/bin/pgctl-2015 restart ''' assert second_stdout == '' assert second.returncode == 1
def it_logs_continuously_when_run_interactively(self, in_example_dir): check_call(('pgctl', 'start')) # this pty simulates running in a terminal read, write = os.openpty() pty.normalize_newlines(read) p = Popen(('pgctl', 'log'), stdout=write, stderr=write) os.close(write) import fcntl fl = fcntl.fcntl(read, fcntl.F_GETFL) fcntl.fcntl(read, fcntl.F_SETFL, fl | os.O_NONBLOCK) assert p.poll() is None # it's still running # needs to loop for several seconds because the default event loop # in tail-f is one second. # TODO: buf is a list, use wait_for() to append to it limit = 3.0 wait = .1 buf = b'' while True: try: block = os.read(read, 1024) print('BLOCK:', block) except OSError as error: print('ERROR:', error) if error.errno == 11: # other end didn't write yet if limit > 0: import time time.sleep(wait) limit -= wait continue else: break else: raise buf += block from testfixtures import StringComparison as S buf = norm.pgctl(buf.decode('UTF-8')) print('NORMED:') print(buf) assert buf == S('''(?s)\ ==> playground/ohhi/log <== {TIMESTAMP} [oe].* ==> playground/sweet/log <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error ==> playground/ohhi/log <== .*{TIMESTAMP} .*$''') assert p.poll() is None # it's still running p.terminate() assert p.wait() == -15
def it_disables_polling_heartbeat(): from mock import patch with patch.dict(os.environ, [('PGCTL_TIMEOUT', '5')]): proc = Popen(('pgctl-2015', 'debug', 'slow-startup'), stdin=open(os.devnull), stdout=PIPE, stderr=PIPE) from testing.assertions import wait_for wait_for(lambda: assert_svstat('playground/slow-startup', state='ready')) check_call(('pgctl-2015', 'stop')) stdout, stderr = proc.communicate() stdout, stderr = stdout.decode('UTF-8'), stderr.decode('UTF-8') assert stderr == '''\ [pgctl] Stopping: slow-startup [pgctl] Stopped: slow-startup pgctl-poll-ready: service's ready check succeeded pgctl-poll-ready: heartbeat is disabled during debug -- quitting ''' assert stdout == '' assert proc.returncode == 0
def it_logs_continuously_when_run_interactively(self, in_example_dir): check_call(('pgctl', 'start')) # this pty simulates running in a terminal read, write = os.openpty() pty.normalize_newlines(read) p = Popen(('pgctl', 'log'), stdout=write, stderr=write) os.close(write) import fcntl fl = fcntl.fcntl(read, fcntl.F_GETFL) fcntl.fcntl(read, fcntl.F_SETFL, fl | os.O_NONBLOCK) assert p.poll() is None # it's still running # needs to loop for several seconds because the default event loop # in tail-f is one second. # TODO: buf is a list, use wait_for() to append to it limit = 3.0 wait = .1 buf = b'' while True: try: block = os.read(read, 1024) print('BLOCK:', block) except OSError as error: print('ERROR:', error) if error.errno == 11: # other end didn't write yet if limit > 0: import time time.sleep(wait) limit -= wait continue else: break else: raise buf += block from testfixtures import StringComparison as S buf = norm.pgctl(buf.decode('UTF-8')) print('NORMED:') print(buf) assert buf == S('''(?s)\ ==> playground/ohhi/logs/current <== {TIMESTAMP} [oe].* ==> playground/sweet/logs/current <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error ==> playground/ohhi/logs/current <== .*{TIMESTAMP} .*$''') assert p.poll() is None # it's still running p.terminate() assert p.wait() == -15
def it_is_disallowed(): assert_command( ('pgctl', 'start'), '', '''\ [pgctl] Starting: sweet [pgctl] Started: sweet ''', 0, ) first = Popen(('pgctl', 'restart'), stdout=PIPE, stderr=PIPE) # slow-shutdown takes two seconds to shut down; aim for the middle: sleep(1) second = Popen(('pgctl', 'restart'), stdout=PIPE, stderr=PIPE) first_stdout, first_stderr = first.communicate() first_stdout, first_stderr = first_stdout.decode('UTF-8'), first_stderr.decode('UTF-8') show_both(first_stdout, first_stderr) assert norm.pgctl(first_stderr) == '''\ [pgctl] Stopping: sweet [pgctl] ERROR: service 'sweet' failed to stop after {TIME} seconds, its status is ready (pid {PID}) {TIME} seconds ==> playground/sweet/log <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error [pgctl] [pgctl] There might be useful information further up in the log; you can view it by running: [pgctl] less +G playground/sweet/log [pgctl] ERROR: Some services failed to stop: sweet ''' assert first_stdout == '' assert first.returncode == 1 second_stdout, second_stderr = second.communicate() second_stdout, second_stderr = second_stdout.decode('UTF-8'), second_stderr.decode('UTF-8') show_both(second_stdout, second_stderr) assert norm.pgctl(second_stderr) == '''\ [pgctl] ERROR: another pgctl command is currently managing this service: (playground/sweet/.pgctl.lock) {PS-HEADER} {PS-STATS} ${PREFIX}/bin/python ${PREFIX}/bin/pgctl restart ''' assert second_stdout == '' assert second.returncode == 1