allow to use multiple upload and report targets with the same name

In this feature target option must allways point to section name instead
of type. Type will be read from type option. In case if type option is
not presented it will try to check if section with architecture exists
(e.g. target = email, section = email:x86_64); if it does, the correct
section name and type will be used. Otherwise it will check if the
specified section exists; if it does, seection name and type will be
returned.
This commit is contained in:
2021-10-17 05:33:23 +03:00
parent a5a99ec0b8
commit 56b77a84a6
17 changed files with 274 additions and 131 deletions

View File

@ -24,7 +24,7 @@ import logging
from logging.config import fileConfig
from pathlib import Path
from typing import Any, Dict, List, Optional, Type
from typing import Any, Dict, List, Optional, Tuple, Type
from ahriman.core.exceptions import InitializeException
@ -42,7 +42,7 @@ class Configuration(configparser.RawConfigParser):
DEFAULT_LOG_FORMAT = "[%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d] [%(funcName)s]: %(message)s"
DEFAULT_LOG_LEVEL = logging.DEBUG
ARCHITECTURE_SPECIFIC_SECTIONS = ["build", "html", "rsync", "s3", "sign", "web"]
ARCHITECTURE_SPECIFIC_SECTIONS = ["build", "sign", "web"]
def __init__(self) -> None:
"""
@ -121,6 +121,26 @@ class Configuration(configparser.RawConfigParser):
def getpath(self, *args: Any, **kwargs: Any) -> Path: ...
def gettype(self, section: str, architecture: str) -> Tuple[str, str]:
"""
get type variable with fallback to old logic
Despite the fact that it has same semantics as other get* methods, but it has different argument list
:param section: section name
:param architecture: repository architecture
:return: section name and found type name
"""
group_type = self.get(section, "type", fallback=None) # new-style logic
if group_type is not None:
return section, group_type
# okay lets check for the section with architecture name
full_section = self.section_name(section, architecture)
if self.has_section(full_section):
return full_section, section
# okay lets just use section as type
if not self.has_section(section):
raise configparser.NoSectionError(section)
return section, section
def load(self, path: Path) -> None:
"""
fully load configuration

View File

@ -45,27 +45,28 @@ class Email(Report, JinjaTemplate):
:ivar user: username to authenticate via SMTP
"""
def __init__(self, architecture: str, configuration: Configuration) -> None:
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
"""
default constructor
:param architecture: repository architecture
:param configuration: configuration instance
:param section: settings section name
"""
Report.__init__(self, architecture, configuration)
JinjaTemplate.__init__(self, "email", configuration)
JinjaTemplate.__init__(self, section, configuration)
self.full_template_path = configuration.getpath("email", "full_template_path", fallback=None)
self.template_path = configuration.getpath("email", "template_path")
self.full_template_path = configuration.getpath(section, "full_template_path", fallback=None)
self.template_path = configuration.getpath(section, "template_path")
# base smtp settings
self.host = configuration.get("email", "host")
self.no_empty_report = configuration.getboolean("email", "no_empty_report", fallback=True)
self.password = configuration.get("email", "password", fallback=None)
self.port = configuration.getint("email", "port")
self.receivers = configuration.getlist("email", "receivers")
self.sender = configuration.get("email", "sender")
self.ssl = SmtpSSLSettings.from_option(configuration.get("email", "ssl", fallback="disabled"))
self.user = configuration.get("email", "user", fallback=None)
self.host = configuration.get(section, "host")
self.no_empty_report = configuration.getboolean(section, "no_empty_report", fallback=True)
self.password = configuration.get(section, "password", fallback=None)
self.port = configuration.getint(section, "port")
self.receivers = configuration.getlist(section, "receivers")
self.sender = configuration.get(section, "sender")
self.ssl = SmtpSSLSettings.from_option(configuration.get(section, "ssl", fallback="disabled"))
self.user = configuration.get(section, "user", fallback=None)
def _send(self, text: str, attachment: Dict[str, str]) -> None:
"""

View File

@ -31,17 +31,18 @@ class HTML(Report, JinjaTemplate):
:ivar report_path: output path to html report
"""
def __init__(self, architecture: str, configuration: Configuration) -> None:
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
"""
default constructor
:param architecture: repository architecture
:param configuration: configuration instance
:param section: settings section name
"""
Report.__init__(self, architecture, configuration)
JinjaTemplate.__init__(self, "html", configuration)
JinjaTemplate.__init__(self, section, configuration)
self.report_path = configuration.getpath("html", "path")
self.template_path = configuration.getpath("html", "template_path")
self.report_path = configuration.getpath(section, "path")
self.template_path = configuration.getpath(section, "template_path")
def generate(self, packages: Iterable[Package], built_packages: Iterable[Package]) -> None:
"""

View File

@ -53,16 +53,17 @@ class Report:
load client from settings
:param architecture: repository architecture
:param configuration: configuration instance
:param target: target to generate report (e.g. html)
:param target: target to generate report aka section name (e.g. html)
:return: client according to current settings
"""
provider = ReportSettings.from_option(target)
section, provider_name = configuration.gettype(target, architecture)
provider = ReportSettings.from_option(provider_name)
if provider == ReportSettings.HTML:
from ahriman.core.report.html import HTML
return HTML(architecture, configuration)
return HTML(architecture, configuration, section)
if provider == ReportSettings.Email:
from ahriman.core.report.email import Email
return Email(architecture, configuration)
return Email(architecture, configuration, section)
return cls(architecture, configuration) # should never happen
def generate(self, packages: Iterable[Package], built_packages: Iterable[Package]) -> None:

View File

@ -36,15 +36,16 @@ class Github(HttpUpload):
:ivar gh_repository: github repository name
"""
def __init__(self, architecture: str, configuration: Configuration) -> None:
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
"""
default constructor
:param architecture: repository architecture
:param configuration: configuration instance
:param section: settings section name
"""
HttpUpload.__init__(self, architecture, configuration, "github")
self.gh_owner = configuration.get("github", "owner")
self.gh_repository = configuration.get("github", "repository")
HttpUpload.__init__(self, architecture, configuration, section)
self.gh_owner = configuration.get(section, "owner")
self.gh_repository = configuration.get(section, "repository")
def asset_remove(self, release: Dict[str, Any], name: str) -> None:
"""

View File

@ -35,15 +35,16 @@ class Rsync(Upload):
_check_output = check_output
def __init__(self, architecture: str, configuration: Configuration) -> None:
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
"""
default constructor
:param architecture: repository architecture
:param configuration: configuration instance
:param section: settings section name
"""
Upload.__init__(self, architecture, configuration)
self.command = configuration.getlist("rsync", "command")
self.remote = configuration.get("rsync", "remote")
self.command = configuration.getlist(section, "command")
self.remote = configuration.get(section, "remote")
def sync(self, path: Path, built_packages: Iterable[Package]) -> None:
"""

View File

@ -37,15 +37,15 @@ class S3(Upload):
:ivar chunk_size: chunk size for calculating checksums
"""
def __init__(self, architecture: str, configuration: Configuration) -> None:
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
"""
default constructor
:param architecture: repository architecture
:param configuration: configuration instance
"""
Upload.__init__(self, architecture, configuration)
self.bucket = self.get_bucket(configuration)
self.chunk_size = configuration.getint("s3", "chunk_size", fallback=8 * 1024 * 1024)
self.bucket = self.get_bucket(configuration, section)
self.chunk_size = configuration.getint(section, "chunk_size", fallback=8 * 1024 * 1024)
@staticmethod
def calculate_etag(path: Path, chunk_size: int) -> str:
@ -70,17 +70,18 @@ class S3(Upload):
return f"{checksum.hexdigest()}{suffix}"
@staticmethod
def get_bucket(configuration: Configuration) -> Any:
def get_bucket(configuration: Configuration, section: str) -> Any:
"""
create resource client from configuration
:param configuration: configuration instance
:param section: settings section name
:return: amazon client
"""
client = boto3.resource(service_name="s3",
region_name=configuration.get("s3", "region"),
aws_access_key_id=configuration.get("s3", "access_key"),
aws_secret_access_key=configuration.get("s3", "secret_key"))
return client.Bucket(configuration.get("s3", "bucket"))
region_name=configuration.get(section, "region"),
aws_access_key_id=configuration.get(section, "access_key"),
aws_secret_access_key=configuration.get(section, "secret_key"))
return client.Bucket(configuration.get(section, "bucket"))
@staticmethod
def files_remove(local_files: Dict[Path, str], remote_objects: Dict[Path, Any]) -> None:

View File

@ -57,16 +57,17 @@ class Upload:
:param target: target to run sync (e.g. s3)
:return: client according to current settings
"""
provider = UploadSettings.from_option(target)
section, provider_name = configuration.gettype(target, architecture)
provider = UploadSettings.from_option(provider_name)
if provider == UploadSettings.Rsync:
from ahriman.core.upload.rsync import Rsync
return Rsync(architecture, configuration)
return Rsync(architecture, configuration, section)
if provider == UploadSettings.S3:
from ahriman.core.upload.s3 import S3
return S3(architecture, configuration)
return S3(architecture, configuration, section)
if provider == UploadSettings.Github:
from ahriman.core.upload.github import Github
return Github(architecture, configuration)
return Github(architecture, configuration, section)
return cls(architecture, configuration) # should never happen
def run(self, path: Path, built_packages: Iterable[Package]) -> None: