def test_one_failed_step_is_preventing_next_steps_from_execution_and_result_is_marked_as_failure(self): """Check the correctness of error handling""" io = IO() str_io = StringIO() buffered = BufferedSystemIO() task_declaration = get_test_declaration() BasicTestingCase.satisfy_task_dependencies(task_declaration.get_task_to_execute(), io=buffered) ctx = ExecutionContext(task_declaration) executor = DeclarativeExecutor() executor.add_step('python', 'this.io().outln("Peter Kropotkin"); return True', task_name=':first', rkd_path='', envs={}) executor.add_step('bash', 'echo "Buenaventura Durruti"; exit 1', task_name=':second', rkd_path='', envs={}) executor.add_step('python', 'this.io().outln("This one will not show"); return True', task_name=':third', rkd_path='', envs={}) with io.capture_descriptors(target_files=[], stream=str_io, enable_standard_out=False): final_result = executor.execute_steps_one_by_one(ctx, task_declaration.get_task_to_execute()) output = str_io.getvalue() + buffered.get_value() self.assertIn('Peter Kropotkin', output) self.assertIn('Buenaventura Durruti', output) self.assertNotIn('This one will not show', output) self.assertEqual(False, final_result)
def mock_execution_context( task: TaskInterface, args: Dict[str, str] = None, env: Dict[str, str] = None, defined_args: Dict[str, dict] = None) -> ExecutionContext: """ Prepares a simplified rkd.api.contract.ExecutionContext instance :param task: :param args: :param env: :param defined_args: :return: """ if args is None: args = {} if env is None: env = {} if defined_args is None: defined_args = {} if args and not defined_args: for name, passed_value in args.items(): defined_args[name] = {'default': ''} return ExecutionContext(TaskDeclaration(task), parent=None, args=args, env=env, defined_args=defined_args)
def test_functional_hooks_are_executed_when_exists_and_files_with_extension_only_are_skipped( self): """Given we have an example hooks in pre-upgrade/whoami.sh and in post-upgrade/history.sh And we try to run those hooks using hooks_executed() Then we will see output produced by those scripts And .dotfiles will be ignored """ self._prepare_test_data() buffer = StringIO() hooks_capturing_io = IO() task = TestTask() task._io = BufferedSystemIO() ctx = ExecutionContext(TaskDeclaration(task), args={}, env={}) with hooks_capturing_io.capture_descriptors(stream=buffer, enable_standard_out=True): with task.hooks_executed(ctx, 'upgrade'): pass self.assertIn('>> This is a whoami.sh hook, test:', buffer.getvalue(), msg='Expected pre-upgrade hook to be ran') self.assertIn('25 June 1978 the rainbow flag was first flown', buffer.getvalue(), msg='Expected post-upgrade hook to be ran') self.assertIn('pre-upgrade/whoami.sh', task._io.get_value()) self.assertNotIn('.gitkeep', task._io.get_value())
def execute_mocked_task_and_get_output(self, task: TaskInterface, args=None, env=None) -> str: """ Run a single task, capturing it's output in a simplified way. There is no whole RKD bootstrapped in this operation. :param TaskInterface task: :param dict args: :param dict env: :return: """ if args is None: args = {} if env is None: env = {} ctx = ApplicationContext([], [], '') ctx.io = BufferedSystemIO() task.internal_inject_dependencies( io=ctx.io, ctx=ctx, executor=OneByOneTaskExecutor(ctx=ctx), temp_manager=TempManager()) merged_env = deepcopy(os.environ) merged_env.update(env) r_io = IO() str_io = StringIO() defined_args = {} for arg, arg_value in args.items(): defined_args[arg] = {'default': ''} with r_io.capture_descriptors(enable_standard_out=True, stream=str_io): try: # noinspection PyTypeChecker result = task.execute( ExecutionContext(TaskDeclaration(task), args=args, env=merged_env, defined_args=defined_args)) except Exception: self._restore_standard_out() print(ctx.io.get_value() + "\n" + str_io.getvalue()) raise return ctx.io.get_value() + "\n" + str_io.getvalue( ) + "\nTASK_EXIT_RESULT=" + str(result)
def _get_prepared_compose_driver(self, args: dict = {}, env: dict = {}) -> ComposeDriver: merged_env = deepcopy(os.environ) merged_env.update(env) task = self.satisfy_task_dependencies(TestTask(), BufferedSystemIO()) declaration = TaskDeclaration(task) ctx = ExecutionContext(declaration, args=args, env=merged_env) return ComposeDriver(task, ctx, TEST_PROJECT_NAME)
def test_non_existing_dir_is_skipped(self): """Assert that non-existing directory does not cause exception, but will be skipped""" task = TestTask() task._io = BufferedSystemIO() task._io.set_log_level('debug') ctx = ExecutionContext(TaskDeclaration(task), args={}, env={}) task.execute_hooks(ctx, 'non-existing-directory') self.assertIn( 'Hooks dir "./hooks.d//non-existing-directory/" not present, skipping', task._io.get_value())
def test_stop_task_executes_stop_task_multiple_times(self): """Test that StopTask will call Driver.stop() multiple times""" task = StopTask() recorded_calls = [] ctx = ExecutionContext(TaskDeclaration(task), args={}, env={}) task.containers( ctx ).stop = lambda service_name, args='', capture=False: recorded_calls.append( service_name) self.execute_mocked_task_and_get_output(task, args={'--profile': ''}) self.assertIn('gateway', recorded_calls) self.assertIn('gateway_letsencrypt', recorded_calls) self.assertIn('gateway_proxy_gen', recorded_calls)
def _prepare_test_for_forking_process(task: TaskInterface = None): if not task: task = TestTask() io = IO() string_io = StringIO() temp = TempManager(chdir='/tmp/') container = ApplicationContext([], [], '') container.io = BufferedSystemIO() executor = OneByOneTaskExecutor(container) declaration = get_test_declaration(task) task._io = io task._io.set_log_level('debug') ctx = ExecutionContext(declaration) return string_io, task, executor, io, ctx, temp
def test_restart_calls_driver_restart_method_on_matched_services(self): """Test calls restart on expected services in expected order""" task = RestartTask() restarted_services = [] ctx = ExecutionContext(TaskDeclaration(task), args={}, env={}) task.containers( ctx ).restart = lambda service_name, args='': restarted_services.append( service_name) self.execute_mocked_task_and_get_output(task, args={ '--profile': 'profile1', '--with-image': False }) self.assertEqual(['gateway', 'website'], restarted_services)
def test_executing_multiple_steps_one_by_one_the_order_is_preserved(self): """Assert that execution order is preserved - as we register steps""" io = IO() str_io = StringIO() task_declaration = get_test_declaration() BasicTestingCase.satisfy_task_dependencies(task_declaration.get_task_to_execute(), io=io) ctx = ExecutionContext(task_declaration) executor = DeclarativeExecutor() executor.add_step('python', 'this.io().outln("First"); return True', task_name=':first', rkd_path='', envs={}) executor.add_step('bash', 'echo "Second"; exit 0', task_name=':second', rkd_path='', envs={}) executor.add_step('python', 'this.io().outln("Third"); return True', task_name=':third', rkd_path='', envs={}) with io.capture_descriptors(target_files=[], stream=str_io, enable_standard_out=False): executor.execute_steps_one_by_one(ctx, task_declaration.get_task_to_execute()) self.assertEqual("First\nSecond\r\nThird\n", str_io.getvalue())
def test_functional_execute_hooks_executes_post_upgrade_hooks(self): """Assert that post-upgrade/history.sh is executed""" self._prepare_test_data() buffer = StringIO() hooks_capturing_io = IO() task = TestTask() task._io = BufferedSystemIO() ctx = ExecutionContext(TaskDeclaration(task), args={}, env={}) with hooks_capturing_io.capture_descriptors(stream=buffer, enable_standard_out=True): task.execute_hooks(ctx, 'post-upgrade') self.assertIn('25 June 1978 the rainbow flag was first flown', buffer.getvalue(), msg='Expected post-upgrade hook to be ran')
def test_requirements_are_written(self): """Verify that basic requirements list is written into requirements.txt file""" backup_dir_path = os.getcwd() try: with tempfile.TemporaryDirectory() as dir_path: os.chdir(dir_path) task = CreateHarborStructureTask() task._io = IO() task.on_requirements_txt_write(ExecutionContext(get_test_declaration())) with open(dir_path + '/requirements.txt', 'rb') as requirements_txt: content = requirements_txt.read().decode('utf-8') self.assertIn('ansible>=2.8', content, msg='Expected Ansible defined in requirements.txt') self.assertIn('rkd-harbor==', content, msg='Expected rkd-harbor to be defined in requirements.txt') finally: os.chdir(backup_dir_path)
def _create_callable_tester(code: str, language: str, io: IO = None) -> bool: if not io: io = IO() executor = DeclarativeExecutor() declaration = get_test_declaration() declaration.get_task_to_execute()._io = io ctx = ExecutionContext(declaration) step = Step( language=language, task_name=':test', code=code, envs={}, task_num=1, rkd_path='' ) if language == 'python': return executor.execute_python_step(ctx, declaration.get_task_to_execute(), step) else: return executor.execute_bash_step(ctx, declaration.get_task_to_execute(), step)