Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service abstraction #1855

Merged
merged 2 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/bci_build/package/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from bci_build.registry import ApplicationCollectionRegistry
from bci_build.registry import Registry
from bci_build.registry import publish_registry
from bci_build.service import Service
from bci_build.templates import DOCKERFILE_TEMPLATE
from bci_build.templates import INFOHEADER_TEMPLATE
from bci_build.templates import KIWI_TEMPLATE
Expand Down Expand Up @@ -107,6 +108,21 @@ def __post_init__(self) -> None:
if self.file_name and "readme" in self.file_name.lower():
raise ValueError(f"Cannot replace variables in {self.file_name}!")

def to_service(self, default_file_name: str) -> Service:
"""Convert this replacement into a
:py:class:`~bci__build.service.Service`.

"""
return Service(
name="replace_using_package_version",
param=[
("file", self.file_name or default_file_name),
("regex", self.regex_in_build_description),
("package", self.package_name),
]
+ ([("parse-version", self.parse_version)] if self.parse_version else []),
)


def _build_tag_prefix(os_version: OsVersion) -> str:
if os_version == OsVersion.TUMBLEWEED:
Expand Down
41 changes: 41 additions & 0 deletions src/bci_build/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""This module includes an abstraction over source services in the Open Build
Service.

"""

import xml.etree.ElementTree as ET
from dataclasses import dataclass
from dataclasses import field
from typing import Literal


@dataclass(kw_only=True, frozen=True)
class Service:
"""Representation of an arbitrary source service in the Open Build Service."""

#: name of this service
name: str
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move this to the beginning, e.g. before mode? I prefer to have non-optional dataclass members first


#: unsorted list of parameters of this source service as a list of tuples
#: where the first value is the parameter's name and the second is the
#: parameter's value
param: list[tuple[str, str]] = field(default_factory=list)

#: service mode (i.e. when the service runs)
mode: Literal["buildtime"] = "buildtime"

def as_xml_element(self) -> ET.Element:
"""Coverts this source service into a
:py:class:`~xml.etree.ElementTree.Element`.

"""
root = ET.Element("service", attrib={"name": self.name, "mode": self.mode})

for param in self.param:
(p := ET.Element("param", attrib={"name": param[0]})).text = param[1]
root.append(p)

return root

def __str__(self) -> str:
return ET.tostring(self.as_xml_element()).decode("utf-8")
58 changes: 58 additions & 0 deletions tests/test_service.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,69 @@
import pytest

from bci_build.container_attributes import BuildType
from bci_build.containercrate import ContainerCrate
from bci_build.os_version import OsVersion
from bci_build.package import DevelopmentContainer
from bci_build.package import ParseVersion
from bci_build.package import Replacement
from bci_build.service import Service
from bci_build.templates import SERVICE_TEMPLATE


def test_service_without_params_as_xml():
assert """<service name="foo" mode="buildtime" />""" == str(Service(name="foo"))


def test_service_with_params_as_xml():
assert (
"""<service name="foo" mode="buildtime"><param name="baz">bar</param><param name="baz">foo</param></service>"""
== str(Service(name="foo", param=[("baz", "bar"), ("baz", "foo")]))
)


@pytest.mark.parametrize(
"replacement, default_file_name, service",
[
# bare bone example
(
Replacement(regex := "%%ver%%", pkg := "pkgFoo"),
"Dockerfile",
Service(
name=(name := "replace_using_package_version"),
param=[("file", "Dockerfile"), ("regex", regex), ("package", pkg)],
),
),
# the default file name is ignored if the parameter file_name is given
(
Replacement(regex, pkg, file_name=(fname := "testfile")),
"Dockerfile",
Service(
name=name,
param=[("file", fname), ("regex", regex), ("package", pkg)],
),
),
# specify a parse_version
(
Replacement(regex, pkg, parse_version=ParseVersion.MAJOR),
"Dockerfile",
Service(
name=name,
param=[
("file", "Dockerfile"),
("regex", regex),
("package", pkg),
("parse-version", "major"),
],
),
),
],
)
def test_replacement_to_service(
replacement: Replacement, default_file_name: str, service: Service
):
assert replacement.to_service(default_file_name) == service


_BASE_KWARGS = {
"name": "test",
"package_name": "test-image",
Expand Down