def on_version_hook_event(self, event): status = event.cargo[HOOK_STATUS] if status == HOOK_STATUS_COMPLETED: version = event.cargo[HOOK_MESSAGE] if self.job.version == version: if settings.hooks.ready: logger.debug("Start ready hook") # Start ready check self.stop_ready_hook = run_hook( settings.hooks.ready, self.root_machine.inbox, args=[self.job.meta], ) else: logger.info("Skip ready hook") self.job_succeeded( JobSuccessStatus.COMPLETE_NO_READY_CHECK.value) self.publish(pysm.Event(JOB_INSTALLATION_COMPLETE)) else: message = f"Expected version '{self.job.version}', got '{version}'" logger.warning(message) self.job_failed(JobFailedStatus.VERSION_MISMATCH.value, message=message) self.publish(pysm.Event(JOB_INSTALLATION_COMPLETE)) elif status in (HOOK_STATUS_FAILED, HOOK_STATUS_TIMED_OUT): error_message = event.cargo[HOOK_MESSAGE] logger.error(f"Version hook failed: {error_message}") self.job_failed(JobFailedStatus.VERSION_HOOK_FAILED.value, message=error_message) self.publish(pysm.Event(JOB_INSTALLATION_COMPLETE))
def on_enter(self, state, event): if self.job.status == JobStatus.QUEUED.value: version_hook = settings.hooks.version force = self.job.force if force or not version_hook: logger.info( f"Skip version check [force={force}, {version_hook if version_hook else 'no-hook'}]" # noqa ) return self._job_verified() logger.debug("Start version check") self.stop_version_hook = run_hook(version_hook, self.root_machine.inbox, args=[self.job.meta]) elif self.job.status == JobStatus.IN_PROGRESS.value: # If the restart is initiated the installation is done if self.job.internal_state == JobProgressStatus.REBOOT_START.value: logger.info("Installation done") self.publish( pysm.Event(JOB_INSTALLATION_DONE, **{JOB: self.job})) # Redo the whole update process else: logger.info("Redo job process") return self._job_verified() else: raise Exception(f"Unexpected job status: {self.job.status}")
def test_stop_event(mocker): mock = mocker.patch("upparat.hooks._hook") stop_event = run_hook("dummy", None, join=True) stop_event.set() _, kwargs = mock.call_args assert kwargs["stop_event"].is_set()
def test_subprocess_args(mocker): mock = _subprocess_mock(mocker, [0], []) default_timer = mocker.Mock(side_effect=[0, 10]) mocker.patch("upparat.hooks.default_timer", default_timer) elapsed = 10 # 10 - 0 command = "noop" retry_count = 0 run_hook(command, mocker.MagicMock(), args=None, join=True) mock.assert_called_once_with( [command, str(elapsed), str(retry_count)], bufsize=1, stdout=subprocess.PIPE, universal_newlines=True, )
def test_fail(mocker): command = "fail" exit_code = 33 mock = _subprocess_mock(mocker, [exit_code], []) queue = mocker.MagicMock() run_hook(command, queue, join=True) assert mock.call_count == 1 args, _ = queue.put.call_args event = args[0] assert event.name == HOOK assert event.cargo[HOOK_STATUS] == HOOK_STATUS_FAILED assert event.cargo[HOOK_MESSAGE] == f"Exit code: {exit_code}"
def on_enter(self, state, event): if not settings.hooks.version: logger.info("Skip version check") self.job_succeeded( JobSuccessStatus.COMPLETE_NO_VERSION_CHECK.value) self.publish(pysm.Event(JOB_INSTALLATION_COMPLETE)) else: # Start version check logger.debug("Start version check") self.stop_version_hook = run_hook(settings.hooks.version, self.root_machine.inbox, args=[self.job.meta])
def test_retry(mocker): command = "noop" retry_count = 2 return_value = "0.1.1" mock = _subprocess_mock(mocker, [RETRY_EXIT_CODE, 0], [return_value]) queue = mocker.MagicMock() settings.hooks.retry_interval = 0 run_hook(command, queue, join=True) assert mock.call_count == retry_count args, _ = queue.put.call_args event = args[0] assert event.name == HOOK assert event.cargo[HOOK_STATUS] == HOOK_STATUS_COMPLETED assert event.cargo[HOOK_MESSAGE] == return_value
def test_retry_timeout(mocker): command = "long" mock = _subprocess_mock(mocker, [RETRY_EXIT_CODE, RETRY_EXIT_CODE], []) queue = mocker.MagicMock() settings.hooks.retry_interval = 0 settings.hooks.max_retries = 1 run_hook(command, queue, join=True) assert mock.call_count == settings.hooks.max_retries args, _ = queue.put.call_args event = args[0] assert event.name == HOOK assert event.cargo[HOOK_STATUS] == HOOK_STATUS_TIMED_OUT assert (event.cargo[HOOK_MESSAGE] == f"Timeout after {settings.hooks.retry_interval}s")
def on_enter(self, state, event): hook = settings.hooks.download force = self.job.force if hook and not force: self.stop_download_hook = run_hook( hook, self.root_machine.inbox, args=[self.job.meta] ) else: logger.info( f"Skip download hook: Hook={hook if hook else 'no-hook'}, force={force}." ) self.start_download_thread()
def on_enter(self, state, event): if settings.hooks.restart: logger.info("Initiate restart") self.job_progress(JobProgressStatus.REBOOT_START.value) self.stop_restart_hook = run_hook( settings.hooks.restart, self.root_machine.inbox, args=[self.job.meta, self.job.force], ) else: logger.info("No restart hook provided") self.job_succeeded(JobSuccessStatus.NO_RESTART_HOOK_PROVIDED.value) self.publish(pysm.Event(RESTART_INTERRUPTED))
def on_enter(self, state, event): # Start install hook if settings.hooks.install: logger.info("Start installation") self.job_progress(JobProgressStatus.INSTALLATION_START.value) self.stop_install_hook = run_hook( settings.hooks.install, self.root_machine.inbox, args=[self.job.meta, self.job.filepath], ) else: logger.info("No installation hook provided") # mark as succeeded because maybe there is no "install" step # necessary since we only want to distribute a file self.job_succeeded( JobSuccessStatus.NO_INSTALLATION_HOOK_PROVIDED.value) self.publish(pysm.Event(INSTALLATION_INTERRUPTED))