pyinfra/mini_queue/utils/storage.py
Julius Unverfehrt 4edf04ab24 Pull request #1: Add storage handle
Merge in RR/mini_queue from add-storage-handle to master

Squashed commit of the following:

commit 03e542d2a65802c28735873fae184209f0c83553
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 11:55:34 2022 +0100

    Quickfix typo

commit b4d538e9445187435d87c5cf8ce1f4e448021129
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 11:41:42 2022 +0100

    added prefetch count and make channel function

commit d46d1375e387d36641c06b062a8ccc54f114ef4c
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 11:20:39 2022 +0100

    black on M.s request

commit bc47b20312a978f19b08531804bf42b00f0a88f0
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 11:19:57 2022 +0100

    changed response

commit 9a475ecd8df9ca007e5f7fe146483b6403eccc3b
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 10:15:08 2022 +0100

    .

commit 108bc3ea90d867575db8c1b1503c9df859222485
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 09:56:56 2022 +0100

    quickrestore

commit ae04d17d8d041f612d86117e8e96c96ddffcbde3
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 09:37:30 2022 +0100

    refactor

commit 68051a72eb93868eba8adba234258b9e5373ecaa
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 08:50:59 2022 +0100

    added answer file template for rancher

commit 09ef45ead51c07732a20133acad0b8b2ae7d0a61
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 08:26:05 2022 +0100

    Quickfix inconsistency

commit d925b0f3f91f29403c88fb6149566ec966af2973
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Wed Feb 16 08:20:40 2022 +0100

    Quick refactor

commit 48795455cde8d97ed98e58c3004a87a26f331352
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Tue Feb 15 17:46:45 2022 +0100

    bluckckck

commit 80e58efab0269dc513990f83b14ceb36b3e4dd8e
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Tue Feb 15 17:45:49 2022 +0100

    Quick restatus setting

commit 83f276ee13348a678b7da84e25ca844dd348b4c9
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Tue Feb 15 17:30:16 2022 +0100

    Quickreset to working status

commit d44cdcf922250639a6832cc3e16d0d967d9853fb
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date:   Tue Feb 15 14:44:26 2022 +0100

    added storage handle for minio WIP
2022-02-16 11:57:52 +01:00

180 lines
5.7 KiB
Python

import abc
import gzip
import logging
import os
from itertools import repeat
from operator import attrgetter
from typing import Iterable
from mini_queue.utils.file import path_to_compressed_storage_pdf_object_name, provide_directory
from mini_queue.utils.meta import NoAttemptsLeft, max_attempts
class StorageHandle:
"""Storage API base"""
def __init__(self):
self.default_container_name = None
@abc.abstractmethod
def __provide_container(self, container_name):
pass
@abc.abstractmethod
def __add_file(self, path, filename, container_name=None):
pass
@max_attempts(n_attempts=10, max_timeout=60)
def add_file(self, path: str, folder: str = None, container_name: str = None) -> None:
"""Adds a file to the store.
Args:
path: Path to file to add to store.
folder: Folder to hold file.
container_name: container to hold file.
"""
storage_path = self.__storage_path(path, folder=folder)
self.__add_file(path, storage_path, container_name)
@max_attempts()
def _list_files(self, object_name_attr="object_name", container_name=None) -> Iterable[str]:
"""List all files in a container.
Args:
container_name: container to list files from.
Returns:
Iterable of filenames.
"""
if container_name is None:
container_name = self.default_container_name
return map(attrgetter(object_name_attr), self.get_objects(container_name))
@abc.abstractmethod
def list_files(self, container_name=None) -> Iterable[str]:
pass
@abc.abstractmethod
def get_objects(self, container_name=None):
pass
@abc.abstractmethod
def __list_containers(self):
pass
@max_attempts()
def get_all_objects(self) -> Iterable:
"""Gets all objects in the store
Returns:
Iterable over all objects in the store.
"""
for container in self.__list_containers():
yield from zip(repeat(container), self.get_objects(container.name))
@abc.abstractmethod
def __purge(self) -> None:
pass
@max_attempts()
def purge(self) -> None:
self.__purge()
def list_files_by_type(self, container_name=None, extension=".pdf.gz"):
return filter(lambda p: p.endswith(extension), self.list_files(container_name))
@abc.abstractmethod
def __fget_object(self, *args, **kwargs):
pass
@staticmethod
def __storage_path(path, folder: str = None):
def path_to_filename(path):
return os.path.basename(path)
storage_path = path_to_filename(path)
if folder is not None:
storage_path = os.path.join(folder, storage_path)
return storage_path
@max_attempts()
def list_folders_and_files(self, container_name: str = None) -> Iterable[str]:
"""Lists pairs of folder name (dossier-IDs) and file name (file-IDs) of items in a container.
Args:
container_name: container to list items for.
Returns:
Iterable of pairs folder name (dossier-ID) and file names (file-ID)
"""
return map(lambda p: p.split("/"), self.list_files_by_type(container_name))
@abc.abstractmethod
def __remove_file(self, folder: str, filename: str, container_name: str = None) -> None:
pass
@max_attempts()
def remove_file(self, folder: str, filename: str, container_name: str = None) -> None:
self.__remove_file(folder, filename, container_name)
def add_file_compressed(self, path, folder: str = None, container_name: str = None) -> None:
"""Adds a file as a .gz archive to the store.
Args:
path: Path to file to add to store.
folder: Folder to hold file.
container_name: container to hold file.
"""
def compress(path_in: str, path_out: str):
with open(path_in, "rb") as f_in, gzip.open(path_out, "wb") as f_out:
f_out.writelines(f_in)
path_gz = path_to_compressed_storage_pdf_object_name(path)
compress(path, path_gz)
self.add_file(path_gz, folder, container_name)
os.unlink(path_gz)
@max_attempts()
def download_file(self, object_names: str, target_root_dir: str, container_name: str = None) -> str:
"""Downloads a file from the store.
Args:
object_names: Complete object name (folder and file).
target_root_dir: Root directory to download file into (including its folder).
container_name: container to load file from.
Returns:
str: Path to downloaded file.
"""
@max_attempts(5, exceptions=(FileNotFoundError,))
def download(object_name: str) -> str:
path, basename = os.path.split(object_name)
target_dir = os.path.join(target_root_dir, path)
provide_directory(target_dir)
target_path = os.path.join(target_dir, basename)
logging.log(msg=f"Downloading {object_name}...", level=logging.DEBUG)
try:
self.__fget_object(container_name, object_name, target_path)
logging.log(msg=f"Downloaded {object_name}.", level=logging.DEBUG)
except Exception as err:
logging.log(msg=f"Downloading {object_name} failed.", level=logging.ERROR)
raise err
return target_path
if container_name is None:
container_name = self.default_container_name
try:
target_path = download(object_names)
except NoAttemptsLeft as err:
logging.log(msg=f"{err}", level=logging.ERROR)
raise err
return target_path