Beispiel #1
0
    def on_version_hook_event(self, _, event):
        # Only handle version hook events
        if event.cargo[HOOK_COMMAND] != settings.hooks.version:
            return

        # hook should never run on force, force == skip it
        assert not self.job.force

        status = event.cargo[HOOK_STATUS]

        if status == HOOK_STATUS_COMPLETED:
            logger.debug("Version hook done")
            version = event.cargo[HOOK_MESSAGE]
            # Check if we do not already run on the version to be installed
            if self.job.version == version:
                logger.info(f"Version {self.job.version} is already running.")
                self.job_succeeded(
                    JobSuccessStatus.VERSION_ALREADY_INSTALLED.value)
                return self.publish(pysm.Event(JOB_REVOKED))
            else:
                logger.info(
                    f"Running on version {version}. Install {self.job.version}"
                )
                return self._job_verified()
        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)
            return self.publish(pysm.Event(JOB_REVOKED))
Beispiel #2
0
    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}")
Beispiel #3
0
 def command_to_event(self, spieler_name, command):
     # please stick to the convention that event identifiers are the same
     # as the command strings
     if command == "einwerfen":
         event = pysm.Event("einwerfen", spieler_name=spieler_name)
     elif command == "wuerfeln":
         event = pysm.Event("wuerfeln", spieler_name=spieler_name)
     elif command == "stechen":
         event = pysm.Event("stechen", spieler_name=spieler_name)
     elif command == "weiter":
         event = pysm.Event("weiter", spieler_name=spieler_name)
     elif command == "beiseite":
         event = pysm.Event("beiseite", spieler_name=spieler_name)
     else:
         raise FalscheAktion
     self.dispatch(event)
Beispiel #4
0
    def on_restart_hook_event(self, _, event):
        # Only handle restart hook events
        if event.cargo[HOOK_COMMAND] != settings.hooks.restart:
            return

        status = event.cargo[HOOK_STATUS]

        if status == HOOK_STATUS_COMPLETED:
            logger.info("Restart hook done")
            self.job_succeeded(JobSuccessStatus.COMPLETE_SOFT_RESTART.value)
            self.publish(pysm.Event(RESTART_INTERRUPTED))
        elif status in (HOOK_STATUS_FAILED, HOOK_STATUS_TIMED_OUT):
            message = event.cargo[HOOK_MESSAGE]
            logger.error(f"Restart failed: {message}")
            self.job_failed(JobFailedStatus.RESTART_HOOK_FAILED.value,
                            message=message)
            self.publish(pysm.Event(RESTART_INTERRUPTED))
Beispiel #5
0
    def on_install_hook_event(self, _, event):
        # Only handle install hook events
        if event.cargo[HOOK_COMMAND] != settings.hooks.install:
            return

        status = event.cargo[HOOK_STATUS]

        if status == HOOK_STATUS_COMPLETED:
            logger.info("Installation hook done")
            self.publish(pysm.Event(INSTALLATION_DONE, **{JOB: self.job}))
        elif status == HOOK_STATUS_OUTPUT:
            self.job_progress(
                JobProgressStatus.INSTALLATION_PROGRESS.value,
                message=event.cargo[HOOK_MESSAGE],
            )
        elif status in (HOOK_STATUS_FAILED, HOOK_STATUS_TIMED_OUT):
            error_message = event.cargo[HOOK_MESSAGE]
            logger.error(f"Installation failed: {error_message}")
            self.job_failed(JobFailedStatus.INSTALLATION_HOOK_FAILED.value,
                            message=error_message)
            self.publish(pysm.Event(INSTALLATION_INTERRUPTED))
Beispiel #6
0
 def weiter(self):
     try:
         self.rdm.weiter()
     except RundeVorbei:
         self.spielzeit_status = self.rdm.deckel_verteilen_restliche_spieler(
         )
         if self.beendet():
             self.verlierende = self.spielzeit_status.spieler[0]
             self.root_machine.dispatch(pysm.Event(events.FERTIG_HALBZEIT))
         else:
             self.rdm = RundenDeckelManagement(self.spielzeit_status)
     # naechsten spieler zueruecksetzen
     self._spieler_zuruecksetzen(self.aktiver_spieler)
Beispiel #7
0
 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))
Beispiel #8
0
 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))
Beispiel #9
0
    def on_handle_hooks(self, _, event):
        if event.cargo[HOOK_COMMAND] != settings.hooks.download:
            return

        status = event.cargo[HOOK_STATUS]

        if status == HOOK_STATUS_COMPLETED:
            logger.info("Hook successfully completed. Download now allowed.")
            self.start_download_thread()

        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.DOWNLOAD_HOOK_FAILED.value, message=error_message
            )
            self.publish(pysm.Event(DOWNLOAD_INTERRUPTED))
Beispiel #10
0
 def on_job_cancelled(self, state, event):
     self._stop_hooks()
     self.publish(pysm.Event(JOB_REVOKED))
Beispiel #11
0
 def _job_verified(self):
     self.publish(pysm.Event(JOB_VERIFIED, **{JOB: self.job}))
Beispiel #12
0
 def on_job_cancelled(self, state, event):
     self._stop_hooks()
     self.publish(pysm.Event(INSTALLATION_INTERRUPTED))
Beispiel #13
0
def download(job, stop_download, publish, update_job_progress):
    """
    See https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
    for more information regarding the backoff behaviour (i.e. jitter).
    """
    start_position_bytes = 0

    if os.path.exists(job.filepath):
        start_position_bytes = os.path.getsize(job.filepath)
        logger.info(f"Partial download of {start_position_bytes} bytes found.")

    if stop_download.is_set():
        logger.info(f"Download interrupted [stop event set].")

    request = urllib.request.Request(job.file_url)
    request.add_header("Range", f"bytes={start_position_bytes}-")

    logger.info(f"Downloading job to {job.filepath}.")

    try:
        with urllib.request.urlopen(
            request, timeout=REQUEST_TIMEOUT_SEC
        ) as source, open(job.filepath, "ab") as destination:

            done = False

            while not done and not stop_download.is_set():
                data = source.read(READ_CHUNK_SIZE_BYTES)

                if data:
                    destination.write(data)
                    # make sure everything is written to disk now
                    # https://docs.python.org/3/library/os.html#os.fsync
                    destination.flush()
                    os.fsync(destination)

                    downloaded_bytes = os.fstat(destination.fileno()).st_size
                    logger.debug(f"Downloaded {downloaded_bytes} bytes.")

                    update_job_progress(
                        JobProgressStatus.DOWNLOAD_PROGRESS.value,
                        message=json.dumps({"downloaded_bytes": downloaded_bytes}),
                    )
                else:
                    done = True

        if stop_download.is_set():
            logger.info(f"Download stopped. Removing {job.filepath}.")
            os.remove(job.filepath)

        if done:
            logger.info(f"Download completed.")
            publish(pysm.Event(DOWNLOAD_COMPLETED, **{JOB: job}))

    except HTTPError as http_error:
        if http_error.status == 416:
            publish(pysm.Event(DOWNLOAD_COMPLETED, **{JOB: job}))
        elif http_error.status == 403:
            logger.warning("URL has expired. Starting over.")
            update_job_progress(JobProgressStatus.DOWNLOAD_INTERRUPT.value)
            publish(pysm.Event(DOWNLOAD_INTERRUPTED))
        else:
            logger.error(f"HTTPError {http_error.status}: {http_error.reason}.")
            raise http_error
    except Exception as exception:
        if type(exception) not in RETRYABLE_EXCEPTIONS:
            # see issue #19, instead of hanging on unhanded exception,
            # we try to improve the situation by just starting over
            # since we don't have any better error recovery strategy.
            logger.exception("Unhandled failure. Starting over.")
            update_job_progress(JobProgressStatus.DOWNLOAD_INTERRUPT.value)
            publish(pysm.Event(DOWNLOAD_INTERRUPTED))
        else:
            # let backoff.on_exception handle retry
            # for this exception
            raise exception
Beispiel #14
0
 def on_job_cancelled(self, state, event):
     self.stop_hooks()
     self.stop_download.set()
     self.publish(pysm.Event(DOWNLOAD_INTERRUPTED))
Beispiel #15
0
 def kein_finale(self, state, event):
     if len(self._spielerinnen_unique) == 1:
         self.root_machine.dispatch(pysm.Event(events.FERTIG_HALBZEIT))
Beispiel #16
0
def _publish(inbox, hook, status, message):
    inbox.put(
        pysm.Event(
            HOOK, **{HOOK_COMMAND: hook, HOOK_STATUS: status, HOOK_MESSAGE: message}
        )
    )