def it_shows_stdout_and_stderr(self, in_example_dir): check_call(('pgctl', 'start', 'sweet')) assert_command( ('pgctl', 'log'), '''\ ==> playground/ohhi/logs/current <== ==> playground/sweet/logs/current <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error ''', '', 0, norm=norm.pgctl, ) check_call(('pgctl', 'restart', 'sweet')) assert_command( ('pgctl', 'log'), '''\ ==> playground/ohhi/logs/current <== ==> playground/sweet/logs/current <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error {TIMESTAMP} sweet {TIMESTAMP} sweet_error ''', '', 0, norm=norm.pgctl, )
def it_can_accept_different_environment_variables(self, in_example_dir): check_call(("sh", "-c", "MYVAR=ohhi pgctl-2015 start")) assert_command( ("pgctl-2015", "log"), """\ ==> playground/environment/log <== {TIMESTAMP} ohhi """, "", 0, norm=norm.pgctl, ) check_call(("sh", "-c", "MYVAR=bye pgctl-2015 restart")) assert_command( ("pgctl-2015", "log"), """\ ==> playground/environment/log <== {TIMESTAMP} ohhi {TIMESTAMP} bye """, "", 0, norm=norm.pgctl, )
def it_shows_stdout_and_stderr(self, in_example_dir): check_call(('pgctl', 'start', 'sweet')) assert_command( ('pgctl', 'log'), '''\ ==> playground/ohhi/log <== ==> playground/sweet/log <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error ''', '', 0, norm=norm.pgctl, ) check_call(('pgctl', 'restart', 'sweet')) assert_command( ('pgctl', 'log'), '''\ ==> playground/ohhi/log <== ==> playground/sweet/log <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error {TIMESTAMP} sweet {TIMESTAMP} sweet_error ''', '', 0, norm=norm.pgctl, )
def it_can_start_via_abspath_pgdir(self, in_example_dir, scratch_dir, tmpdir): with tmpdir.ensure_dir("arbitrary").as_cwd(): assert not scratch_dir.join("now.date").isfile() env = os.environ.copy() env["PGCTL_PGDIR"] = str(in_example_dir.join("playground")) check_call(("pgctl-2015", "start", "date"), env=env) wait_for(lambda: scratch_dir.join("now.date").isfile())
def it_can_recover_from_a_git_clean(self, in_example_dir, service_name): def assert_status(): assert_command( ("pgctl-2015", "status"), """\ sleep: down tail: ready (pid {PID}) {TIME} seconds """, "", 0, norm=norm.pgctl, ) check_call(("pgctl-2015", "start", "tail")) assert_status() # simulate a git-clean: blow everything away, create a fresh copy parent_dir = in_example_dir.join("..") with parent_dir.as_cwd(): in_example_dir.remove(rec=True) from testing import copy_example copy_example(service_name, parent_dir) assert_status()
def it_can_recover_from_a_git_clean(self, in_example_dir, service_name): def assert_status(): assert_command( ('pgctl', 'status'), '''\ ● sleep: down ● tail: ready └─ pid: {PID}, {TIME} seconds ''', '', 0, norm=norm.pgctl, ) check_call(('pgctl', 'start', 'tail')) assert_status() # simulate a git-clean: blow everything away, create a fresh copy parent_dir = in_example_dir.join('..') with parent_dir.as_cwd(): in_example_dir.remove(rec=True) from testing import copy_example copy_example(service_name, parent_dir) assert_status()
def it_can_accept_different_environment_variables(self, in_example_dir): check_call(('sh', '-c', 'MYVAR=ohhi pgctl start')) assert_command( ('pgctl', 'log'), '''\ ==> playground/environment/log <== {TIMESTAMP} ohhi ''', '', 0, norm=norm.pgctl, ) check_call(('sh', '-c', 'MYVAR=bye pgctl restart')) assert_command( ('pgctl', 'log'), '''\ ==> playground/environment/log <== {TIMESTAMP} ohhi {TIMESTAMP} bye ''', '', 0, norm=norm.pgctl, )
def it_shows_stdout_and_stderr(self, in_example_dir): check_call(("pgctl-2015", "start", "sweet")) assert_command( ("pgctl-2015", "log"), """\ ==> playground/ohhi/log <== ==> playground/sweet/log <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error """, "", 0, norm=norm.pgctl, ) check_call(("pgctl-2015", "restart", "sweet")) assert_command( ("pgctl-2015", "log"), """\ ==> playground/ohhi/log <== ==> playground/sweet/log <== {TIMESTAMP} sweet {TIMESTAMP} sweet_error {TIMESTAMP} sweet {TIMESTAMP} sweet_error """, "", 0, norm=norm.pgctl, )
def it_can_start_via_abspath_service(self, in_example_dir, scratch_dir, tmpdir): with tmpdir.ensure_dir('arbitrary').as_cwd(): assert not scratch_dir.join('now.date').isfile() date_abspath = str(in_example_dir.join('playground/date')) check_call(('pgctl', 'start', date_abspath)) wait_for(lambda: scratch_dir.join('now.date').isfile())
def it_shows_error_on_stop_for_sweet(self): check_call(('pgctl', 'start', 'sweet')) assert_command( ('pgctl', 'restart', 'sweet'), '', '''\ [pgctl] Stopping: sweet [pgctl] ERROR: service 'sweet' failed to stop after {TIME} seconds, these runaway processes did not stop: {PS-HEADER} {PS-STATS} sleep infinity There are two ways you can fix this: * temporarily: pgctl stop sweet --force * permanently: http://pgctl.readthedocs.org/en/latest/user/quickstart.html#writing-playground-services ==> playground/sweet/logs/current <== {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/logs/current [pgctl] ERROR: Some services failed to stop: sweet ''', 1, norm=norm.pgctl, )
def it_can_start_via_abspath_pgdir(self, in_example_dir, scratch_dir, tmpdir): with tmpdir.ensure_dir('arbitrary').as_cwd(): assert not scratch_dir.join('now.date').isfile() env = os.environ.copy() env['PGCTL_PGDIR'] = str(in_example_dir.join('playground')) check_call(('pgctl', 'start', 'date'), env=env) wait_for(lambda: scratch_dir.join('now.date').isfile())
def it_can_accept_different_environment_variables(self, in_example_dir): check_call(('sh', '-c', 'MYVAR=ohhi pgctl start')) assert_command( ('pgctl', 'log'), '''\ ==> playground/environment/logs/current <== {TIMESTAMP} ohhi ''', '', 0, norm=norm.pgctl, ) check_call(('sh', '-c', 'MYVAR=bye pgctl restart')) assert_command( ('pgctl', 'log'), '''\ ==> playground/environment/logs/current <== {TIMESTAMP} ohhi {TIMESTAMP} bye ''', '', 0, norm=norm.pgctl, )
def it_displays_correctly_when_the_service_is_up(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) assert_command( ('pgctl', 'status', 'sleep'), 'sleep: ready (pid {PID}) {TIME} seconds\n', '', 0, norm=norm.pgctl, )
def it_stops_quickly(): """Tests a regression in pgctl where services using pgctl-poll-ready fail to stop because the background process started by pgctl-poll-ready isn't dying quickly.""" check_call(('pgctl', 'start')) prestop_time = time.time() check_call(('pgctl', 'stop')) poststop_time = time.time() assert poststop_time - prestop_time < 2
def it_does_start(self, in_example_dir): test_string = 'oh, hi there.\n' with open('input', 'w') as input: input.write(test_string) assert not os.path.isfile('output') check_call(('pgctl', 'start', 'tail')) wait_for(lambda: os.path.isfile('output')) assert open('output').read() == test_string
def it_displays_correctly_when_the_service_is_down(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) check_call(('pgctl', 'stop', 'sleep')) assert_command( ('pgctl', 'status', 'sleep'), 'sleep: down\n', '', 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_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_displays_correctly_when_the_service_is_down(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) check_call(('pgctl', 'stop', 'sleep')) assert_command( ('pgctl', 'status', 'sleep'), ' ● sleep: down\n', '', 0, )
def it_does_start(self, in_example_dir): test_string = "oh, hi there.\n" with open("input", "w") as input: input.write(test_string) assert not os.path.isfile("output") check_call(("pgctl-2015", "start", "tail")) wait_for(lambda: os.path.isfile("output")) assert open("output").read() == test_string
def it_displays_correctly_when_the_service_is_up(self, in_example_dir): check_call(('pgctl-2015', 'start', 'sleep')) assert_command( ('pgctl-2015', 'status', 'sleep'), 'sleep: ready (pid {PID}) {TIME} seconds\n', '', 0, norm=norm.pgctl, )
def it_stops_multiple_services(self, in_example_dir): check_call(("pgctl-2015", "start", "sleep", "tail")) assert_svstat("playground/sleep", state="up") assert_svstat("playground/tail", state="up") check_call(("pgctl-2015", "stop", "sleep", "tail")) assert_svstat("playground/sleep", state=SvStat.UNSUPERVISED) assert_svstat("playground/tail", state=SvStat.UNSUPERVISED)
def it_stops_multiple_services(self, in_example_dir): check_call(('pgctl', 'start', 'sleep', 'tail')) assert_svstat('playground/sleep', state='up') assert_svstat('playground/tail', state='up') check_call(('pgctl', 'stop', 'sleep', 'tail')) assert_svstat('playground/sleep', state=SvStat.UNSUPERVISED) assert_svstat('playground/tail', state=SvStat.UNSUPERVISED)
def it_displays_the_status_of_multiple_services(self, in_example_dir): """Expect multiple services with status and PID""" check_call(('pgctl', 'start', 'sleep')) assert_command( ('pgctl', 'status', 'sleep', 'tail'), '''\ sleep: ready (pid {PID}) {TIME} seconds tail: down ''', '', 0, norm=norm.pgctl, )
def it_displays_the_status_of_multiple_services(self, in_example_dir): """Expect multiple services with status and PID""" check_call(("pgctl-2015", "start", "sleep")) assert_command( ("pgctl-2015", "status", "sleep", "tail"), """\ sleep: ready (pid {PID}) {TIME} seconds tail: down """, "", 0, norm=norm.pgctl, )
def it_displays_the_status_of_multiple_services(self, in_example_dir): """Expect multiple services with status and PID""" check_call(('pgctl-2015', 'start', 'sleep')) assert_command( ('pgctl-2015', 'status', 'sleep', 'tail'), '''\ sleep: ready (pid {PID}) {TIME} seconds tail: down ''', '', 0, norm=norm.pgctl, )
def it_displays_the_status_of_all_services(self, in_example_dir): """Expect all services to provide status when no service is specified""" check_call(('pgctl-2015', 'start', 'tail')) assert_command( ('pgctl-2015', 'status'), '''\ sleep: down tail: ready (pid {PID}) {TIME} seconds ''', '', 0, norm=norm.pgctl, )
def it_displays_the_status_of_all_services(self, in_example_dir): """Expect all services to provide status when no service is specified""" check_call(("pgctl-2015", "start", "tail")) assert_command( ("pgctl-2015", "status"), """\ sleep: down tail: ready (pid {PID}) {TIME} seconds """, "", 0, norm=norm.pgctl, )
def it_displays_the_status_of_all_services(self, in_example_dir): """Expect all services to provide status when no service is specified""" check_call(('pgctl', 'start', 'tail')) assert_command( ('pgctl', 'status'), '''\ sleep: down tail: ready (pid {PID}) {TIME} seconds ''', '', 0, norm=norm.pgctl, )
def it_prints_log_stop_info_for_verbose(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) # TODO: Finish this assert_command( ('pgctl', 'stop', 'sleep', '--verbose'), '', '''\ [pgctl] Stopping: sleep [pgctl] Stopped: sleep [pgctl] Stopping logger for: sleep [pgctl] Stopped logger for: sleep ''', 0, )
def it_displays_correctly_when_the_service_is_up_json(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) stdout, stderr, returncode = run( ('pgctl', '--json', 'status', 'sleep'), ) assert returncode == 0 assert json.loads(stdout) == { 'sleep': { 'exitcode': None, 'pid': ANY_INTEGER(), 'process': None, 'seconds': ANY_INTEGER(), 'state': 'ready', }, } assert stderr == ''
def it_also_works_when_up(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) assert_svstat('playground/sleep', state='up') assert_command( ('pgctl', 'restart', 'sleep'), '', '''\ [pgctl] Stopping: sleep [pgctl] Stopped: sleep [pgctl] Starting: sleep [pgctl] Started: sleep ''', 0, ) assert_svstat('playground/sleep', state='up')
def it_displays_correctly_when_the_service_is_up_json( self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) stdout, stderr, returncode = run( ('pgctl', '--json', 'status', 'sleep'), ) assert returncode == 0 assert json.loads(stdout) == { 'sleep': { 'exitcode': None, 'pid': ANY_INTEGER(), 'process': None, 'seconds': ANY_INTEGER(), 'state': 'ready', }, } assert stderr == ''
def it_succeeds_on_second_stop_after_some_delay(self): check_call(('pgctl', 'start')) assert_svstat('playground/sweet', state='up') with pytest.raises(subprocess.CalledProcessError): check_call(('pgctl', 'stop')) time.sleep(3) assert_command( ('pgctl', 'stop'), '', '''\ [pgctl] Already stopped: sweet ''', 0, norm=norm.pgctl, )
def it_fails_by_default(self): check_call(('pgctl-2015', 'start')) assert_svstat('playground/sweet', state='up') assert_command( ('pgctl-2015', 'stop'), '', '''\ [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] ERROR: Some services failed to stop: sweet ''', 1, norm=norm.pgctl, )
def it_succeeds_on_forceful_stop(self): check_call(('pgctl', 'start')) assert_svstat('playground/sweet', state='up') assert_command( ('pgctl', 'stop', '--force'), '', '''\ [pgctl] Stopping: sweet [pgctl] WARNING: Killing these runaway processes at user's request (--force): {PS-HEADER} {PS-STATS} sleep 2.5 Learn why they did not stop: http://pgctl.readthedocs.org/en/latest/user/quickstart.html#writing-playground-services [pgctl] Stopped: sweet ''', 0, norm=norm.pgctl, )
def it_fails_by_default(self): check_call(('pgctl', 'start')) assert_svstat('playground/sweet', state='up') assert_command( ('pgctl', 'stop'), '', '''\ [pgctl] Stopping: sweet [pgctl] ERROR: service 'sweet' failed to stop after {TIME} seconds, its status is ready (pid {PID}) {TIME} seconds ==> playground/sweet/logs/current <== {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/logs/current [pgctl] ERROR: Some services failed to stop: sweet ''', 1, norm=norm.pgctl, )
def it_warns_on_forcelly_stop_for_slow_start(self): check_call(('pgctl', 'start', 'slow-startup')) assert_command( ('pgctl', 'restart', 'slow-startup', '--force'), '', '''\ [pgctl] Stopping: slow-startup [pgctl] WARNING: Killing these runaway processes at user's request (--force): {PS-HEADER} {PS-STATS} sleep 987654 Learn why they did not stop: http://pgctl.readthedocs.org/en/latest/user/quickstart.html#writing-playground-services [pgctl] Stopped: slow-startup [pgctl] Starting: slow-startup [pgctl] Started: slow-startup ''', 0, norm=norm.pgctl, )
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_fails_by_default(self): check_call(('pgctl', 'start')) assert_svstat('playground/sweet', state='up') assert_command( ('pgctl', 'stop'), '', '''\ [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 ''', 1, norm=norm.pgctl, )
def it_displays_the_status_of_multiple_services_json(self, in_example_dir): """Expect multiple services with status and PID""" check_call(('pgctl', 'start', 'sleep')) stdout, stderr, returncode = run( ('pgctl', '--json', 'status', 'sleep', 'tail'), ) assert returncode == 0 assert json.loads(stdout) == { 'sleep': { 'exitcode': None, 'pid': ANY_INTEGER(), 'process': None, 'seconds': ANY_INTEGER(), 'state': 'ready', }, 'tail': { 'exitcode': None, 'pid': None, 'process': None, 'seconds': None, 'state': 'down', } } assert stderr == ''
def it_can_shut_down_successfully(self): # if we configure it to wait a bit longer, it works fine with open('playground/sweet/timeout-stop', 'w') as timeout: timeout.write('3') check_call(('pgctl', 'start')) assert_svstat('playground/sweet', state='up') check_call(('pgctl', 'restart')) assert_svstat('playground/sweet', state='up') check_call(('pgctl', 'stop')) assert_svstat('playground/sweet', state=SvStat.UNSUPERVISED)
def it_does_stop(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) check_call(('pgctl', 'stop', 'sleep')) assert_svstat('playground/sleep', state=SvStat.UNSUPERVISED)
def it_does_start(self, in_example_dir, scratch_dir): assert not scratch_dir.join('now.date').isfile() check_call(('pgctl', 'start', 'date')) wait_for(lambda: scratch_dir.join('now.date').isfile())
def it_is_idempotent(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) check_call(('pgctl', 'start', 'sleep'))
def it_also_works_when_up(self, in_example_dir): check_call(('pgctl', 'start', 'sleep')) assert_svstat('playground/sleep', state='up') self.it_is_just_stop_then_start(in_example_dir)
def it_only_starts_the_indicated_services(self, in_example_dir, request): check_call(('pgctl', 'start', 'sleep')) assert_svstat('playground/sleep', state='up') assert_svstat('playground/tail', state=SvStat.UNSUPERVISED)
def it_starts_multiple_services(self, in_example_dir, request): check_call(('pgctl', 'start', 'sleep', 'tail')) assert_svstat('playground/sleep', state='up') assert_svstat('playground/tail', state='up')
def it_starts_everything_with_no_arguments_no_config( self, in_example_dir, request): check_call(('pgctl', 'start')) assert_svstat('playground/sleep', state='up') assert_svstat('playground/tail', state='up')
def it_first_stops_the_background_service_if_running(): check_call(('pgctl', 'start', 'greeter')) assert_svstat('playground/greeter', state='up') assert_works_interactively()
def it_is_successful_before_start(self, in_example_dir): check_call(('pgctl', 'stop', 'sleep'))