Skip to content

Commit

Permalink
Split ContainerCrate into ContainerCrateAssigner
Browse files Browse the repository at this point in the history
This way ContainerCrate can be instantiated per (os_version,
package_name) and we can handle service_replacements properly.
  • Loading branch information
dirkmueller committed Nov 29, 2024
1 parent 3f5f7b2 commit 6967aee
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 84 deletions.
90 changes: 62 additions & 28 deletions src/bci_build/containercrate.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,56 @@
"""Crate to handle multibuild containers in the generator."""

import asyncio
import os

from bci_build.templates import SERVICE_TEMPLATE
from bci_build.util import write_to_file


class ContainerCrate:
"""ContainerCrate is combining multiple container build flavors.
This provides package-central functions like generating _service and
_multibuild files.
"""
class ContainerCrateAssigner:
"""ContainerCrateAssigner is allocating ContainerCrates for a given list of containers."""

def __init__(self, containers: list):
"""Assign the crate for every container."""
self._all_build_flavors: dict[tuple, set] = {}
"""Assign the ContainerCrate for every container in the list."""
all_build_flavors: dict[tuple, set] = {}
all_containers: dict[tuple, list] = {}

for container in containers:
if container.build_flavor:
self._all_build_flavors.setdefault(
all_build_flavors.setdefault(
(container.os_version, container.package_name), set()
).add(container.build_flavor)
all_containers.setdefault(
(container.os_version, container.package_name), []
).append(container)

for container in containers:
if container.crate is not None:
raise ValueError("Container is already part of a ContainerCrate")
container.crate = self
for os_version, package_name in all_containers:
print(f"os_version {os_version} package_name {package_name}")
crate = ContainerCrate(
sorted(all_build_flavors.get((os_version, package_name), [])),
all_containers[(os_version, package_name)],
)
print(f"created crate with {crate._all_build_flavors}")
for container in all_containers[(os_version, package_name)]:
if container.crate is not None:
raise ValueError("Container is already part of a ContainerCrate")
container.crate = crate


class ContainerCrate:
"""ContainerCrate represents a group of containers that share the same os_version/package_name and
only differ by flavor.
def all_build_flavors(self, container) -> list[str]:
This provides package-central functions like generating _service and
_multibuild files.
"""

def __init__(self, all_build_flavors: list[str], all_containers: list):
"""Assign the crate for every container."""
self._all_build_flavors = all_build_flavors
self._all_containers = all_containers

def all_build_flavors(self) -> list[str]:
"""Return all build flavors for this container in the crate"""
return sorted(
self._all_build_flavors.get(
(container.os_version, container.package_name), [""]
)
)
return self._all_build_flavors

def default_dockerfile(self, container) -> str:
buildrelease: str = ""
Expand All @@ -47,14 +63,34 @@ def default_dockerfile(self, container) -> str:
# For this container we only build the Dockerfile.$flavor builds.
"""

def multibuild(self, container) -> str:
def multibuild(self) -> str:
"""Return the _multibuild file string to write for this ContainerCrate."""
flavors: str = "\n".join(
" " * 4 + f"<package>{pkg}</package>"
for pkg in self.all_build_flavors(container)
" " * 4 + f"<package>{pkg}</package>" for pkg in self.all_build_flavors()
)
return f"<multibuild>\n{flavors}\n</multibuild>"

def services(self) -> str:
services = f"""<services>
<service mode="buildtime" name="{self._all_containers[0].build_recipe_type}_label_helper"/>
<service mode="buildtime" name="kiwi_metainfo_helper"/>
"""
for container in self._all_containers:
for replacement in container.replacements_via_service:
services += (
" "
+ "\n ".join(
str(
replacement.to_service(
f"Dockerfile.{container.build_flavor}"
)
).splitlines()
)
+ "\n"
)
services = services + "</services>"
return services

async def write_files_to_folder(self, dest: str, container) -> list[str]:
"""Write the files that this crate,container pair needs."""
files = []
Expand All @@ -66,12 +102,10 @@ async def write_files_to_folder(self, dest: str, container) -> list[str]:
files.append(fname)

fname = "_multibuild"
await write_to_file(os.path.join(dest, fname), self.multibuild(container))
await write_to_file(os.path.join(dest, fname), self.multibuild())
files.append(fname)

fname = "_service"
await write_to_file(
os.path.join(dest, fname), SERVICE_TEMPLATE.render(image=container)
)
await write_to_file(os.path.join(dest, fname), self.services())
files.append(fname)
return files
4 changes: 2 additions & 2 deletions src/bci_build/package/apache_tomcat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from bci_build.container_attributes import TCP
from bci_build.container_attributes import PackageType
from bci_build.containercrate import ContainerCrate
from bci_build.containercrate import ContainerCrateAssigner
from bci_build.os_version import CAN_BE_LATEST_OS_VERSION
from bci_build.os_version import OsVersion
from bci_build.package import DOCKERFILE_RUN
Expand Down Expand Up @@ -150,4 +150,4 @@ def _get_java_packages(jre_major: int) -> list[str]:
)
]

TOMCAT_CRATE = ContainerCrate(TOMCAT_CONTAINERS)
ContainerCrateAssigner(TOMCAT_CONTAINERS)
5 changes: 2 additions & 3 deletions src/bci_build/package/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Literal

from bci_build.container_attributes import SupportLevel
from bci_build.containercrate import ContainerCrate
from bci_build.containercrate import ContainerCrateAssigner
from bci_build.os_version import CAN_BE_LATEST_OS_VERSION
from bci_build.os_version import _SUPPORTED_UNTIL_SLE
from bci_build.os_version import OsVersion
Expand Down Expand Up @@ -119,8 +119,7 @@ def _get_python_kwargs(py3_ver: _PYTHON_VERSIONS, os_version: OsVersion):
)
for py_version in ("3.9", "3.11")
]

SAC_PYTHON_CRATE = ContainerCrate(SAC_PYTHON_CONTAINERS)
ContainerCrateAssigner(SAC_PYTHON_CONTAINERS)


PYTHON_3_6_CONTAINERS = (
Expand Down
3 changes: 2 additions & 1 deletion src/bci_build/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def as_xml_element(self) -> ET.Element:
(p := ET.Element("param", attrib={"name": param[0]})).text = param[1]
root.append(p)

ET.indent(root, space=" " * 4)
return root

def __str__(self) -> str:
return ET.tostring(self.as_xml_element()).decode("utf-8")
return ET.tostring(self.as_xml_element(), encoding="unicode")
10 changes: 2 additions & 8 deletions src/bci_build/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,24 +201,18 @@
"""<services>
<service mode="buildtime" name="{{ image.build_recipe_type }}_label_helper"/>
<service mode="buildtime" name="kiwi_metainfo_helper"/>
{%- set all_build_flavors = [""] %}
{%- if image.crate and image.build_flavor %}
{%- set all_build_flavors = image.crate.all_build_flavors(image) %}
{%- endif %}
{%- for flavor in all_build_flavors %}
{%- for replacement in image.replacements_via_service %}
<service name="replace_using_package_version" mode="buildtime">
<param name="file">
{%- if replacement.file_name != None %}{{replacement.file_name}}
{%- elif (image.build_recipe_type|string) == "docker" %}{% if flavor %}Dockerfile.{{ flavor }}{% else %}Dockerfile{% endif %}
{%- elif (image.build_recipe_type|string) == "docker" %}Dockerfile
{%- else %}{{ image.package_name }}.kiwi
{%- endif %}</param>
<param name="regex">{{ replacement.regex_in_build_description }}</param>
<param name="package">{{ replacement.package_name }}</param>{% if replacement.parse_version %}
<param name="parse-version">{{ replacement.parse_version }}</param>{% endif %}
</service>
{%- endfor -%}
{% endfor %}
{%- endfor %}
</services>
"""
)
5 changes: 3 additions & 2 deletions tests/test_crate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from bci_build.container_attributes import BuildType
from bci_build.containercrate import ContainerCrate
from bci_build.containercrate import ContainerCrateAssigner
from bci_build.os_version import OsVersion
from bci_build.package import DevelopmentContainer

Expand All @@ -22,8 +22,9 @@ def test_multibuild_with_multi_flavor_docker():
)
for flavor in ("flavor1", "flavor2")
]
ContainerCrateAssigner(containers)
assert (
ContainerCrate(containers).multibuild(containers[0])
containers[0].crate.multibuild()
== """<multibuild>
<package>flavor1</package>
<package>flavor2</package>
Expand Down
44 changes: 4 additions & 40 deletions tests/test_service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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
Expand All @@ -15,10 +14,10 @@ def test_service_without_params_as_xml():


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")]))
)
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(
Expand Down Expand Up @@ -167,38 +166,3 @@ def test_service_with_replacement_docker():
</service>
</services>"""
)


def test_service_with_multi_flavor_docker():
containers = [
DevelopmentContainer(
**_BASE_KWARGS,
build_recipe_type=BuildType.DOCKER,
build_flavor=flavor,
replacements_via_service=[
Replacement(regex_in_build_description="%%my_ver%%", package_name="sh"),
],
)
for flavor in ("flavor1", "flavor2")
]
ContainerCrate(containers)

assert (
SERVICE_TEMPLATE.render(
image=containers[0],
)
== """<services>
<service mode="buildtime" name="docker_label_helper"/>
<service mode="buildtime" name="kiwi_metainfo_helper"/>
<service name="replace_using_package_version" mode="buildtime">
<param name="file">Dockerfile.flavor1</param>
<param name="regex">%%my_ver%%</param>
<param name="package">sh</param>
</service>
<service name="replace_using_package_version" mode="buildtime">
<param name="file">Dockerfile.flavor2</param>
<param name="regex">%%my_ver%%</param>
<param name="package">sh</param>
</service>
</services>"""
)

0 comments on commit 6967aee

Please sign in to comment.