Skip to content

Commit

Permalink
Add new registry section
Browse files Browse the repository at this point in the history
Allow to specify references to OCI containers in the
image description like in the following example:

<registry source="registry.suse.com">
    <container name="some" tag="latest" path="some/path" use_with="podman"/>
    <container name="some_other" use_with="docker" fetch_only="true"/>
</registry>

During the kiwi process the containers are fetched into a
temporary location and a systemd service is configured to
one time load the containers into the local registry at
first boot of the system. This Fixes #2663
  • Loading branch information
schaefi committed Oct 15, 2024
1 parent 72d7860 commit f7bf346
Show file tree
Hide file tree
Showing 16 changed files with 713 additions and 4 deletions.
3 changes: 1 addition & 2 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,7 @@ def setup(app):
html_theme = "sphinx_rtd_theme"

html_theme_options = {
'collapse_navigation': False,
'display_version': False
'collapse_navigation': False
}

# -- Options for manual page output ---------------------------------------
Expand Down
52 changes: 52 additions & 0 deletions doc/source/image_description/elements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,58 @@ Used to customize the installation media images created for oem images
deployment.
For details see: :ref:`installmedia_customize`

.. _sec.registry:

<registry>
----------

Setup containers to fetch from one ore more registry elements

.. code:: xml
<registry source="registry.opensuse.org">
<container name="some" use_with="podman"/>
</registry>
The optional registry element specifies the location of one ore
more containers on a registry `source` server. {kiwi} will take
this information and fetch the containers as OCI archives to
the image. On first boot those container archives will be loaded
into the local container backend store for the selected
backend and the archive files gets deleted.

<registry><container>
---------------------

Details about a container to fetch from a given source registry

.. code:: xml
<registry source="registry.opensuse.org">
<container name="some" use_with="podman"/>
</registry>
The `name` and `use_with` attributes are mandatory and specifies
the name of the container and for which backend it should be used.
So far `docker` and `podman` are supported backend values for
the `use_with` attribute. The `container` element has the following
optional attributes

path="some/path"
The path to the container in the registry. If not specified
the value defaults to `/`

fetch_only="true|false"
If set to `true` kiwi will only fetch the container but does not
setup the systemd unit for actually loading the container into
the local registry. In this mode the container archive file stays
in the system and can be handled in a custom way. By default
`fetch_only` is set to `false`.

tag="tagname"
Specifies the container tag to fetch. If not set the tag name
defaults to `latest`

.. _sec.repository:

<repository>
Expand Down
42 changes: 42 additions & 0 deletions kiwi/builder/template/container_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved.
#
# This file is part of kiwi.
#
# kiwi is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# kiwi is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
#
import os
from string import Template
from textwrap import dedent


class BuilderTemplateSystemdUnit:
"""
**systemd unit file templates**
"""
def get_container_import_template(self) -> Template:
template_data = dedent('''
# kiwi generated unit file
[Unit]
Description=Import Local Container: ${container_name}
ConditionPathExists=${container_file}
[Service]
Type=oneshot
ExecStart=${load_command}
ExecStartPost=/bin/rm ${container_file}
[Install]
WantedBy=multi-user.target
''').strip() + os.linesep
return Template(template_data)
1 change: 1 addition & 0 deletions kiwi/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
) if MODULE_SPEC else 'unknown'

TEMP_DIR = '/var/tmp'
LOCAL_CONTAINERS = '/var/tmp/kiwi_containers'
CUSTOM_RUNTIME_CONFIG_FILE = None
PLATFORM_MACHINE = platform.machine()
EFI_FAT_IMAGE_SIZE = 20
Expand Down
53 changes: 53 additions & 0 deletions kiwi/schema/kiwi.rnc
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ div {
k.drivers* &
k.strip* &
k.repository* &
k.registry* &
k.packages* &
k.extension?
}
Expand Down Expand Up @@ -1047,6 +1048,58 @@ div {
}
}

#==========================================
# common element <registry>
#
div {
k.registry.profiles.attribute = k.profiles.attribute
k.registry.arch.attribute = k.arch.attribute
k.registry.source.attribute =
## Name of registry source server
attribute source { text }
k.registry.attlist =
k.registry.profiles.attribute? &
k.registry.arch.attribute? &
k.registry.source.attribute
k.registry =
element registry {
k.registry.attlist,
k.container+
}
}

#==========================================
# common element <container>
#
div {
k.container.name.attribute =
## Container name
attribute name { text }
k.container.tag.attribute =
## Container tag, defaults to 'latest' if not specified
attribute tag { text }
k.container.path.attribute =
## Container path, default to '/' if not specified
attribute path { text }
k.container.fetch_only.attribute =
## Only fetch the container but do not activate the
## loading of the container at first boot
attribute fetch_only { xsd:boolean }
k.container.use_with.attribute =
## Use container with specified container backend
attribute use_with { "podman" | "docker" }
k.container.attlist =
k.container.name.attribute &
k.container.use_with.attribute &
k.container.tag.attribute? &
k.container.path.attribute? &
k.container.fetch_only.attribute?
k.container =
element container {
k.container.attlist
}
}

#==========================================
# common element <repository>
#
Expand Down
98 changes: 98 additions & 0 deletions kiwi/schema/kiwi.rng
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ named /etc/ImageID</a:documentation>
<zeroOrMore>
<ref name="k.repository"/>
</zeroOrMore>
<zeroOrMore>
<ref name="k.registry"/>
</zeroOrMore>
<zeroOrMore>
<ref name="k.packages"/>
</zeroOrMore>
Expand Down Expand Up @@ -1601,6 +1604,101 @@ definition can be composed by other existing profiles.</a:documentation>
</element>
</define>
</div>
<!--
==========================================
common element <registry>
-->
<div>
<define name="k.registry.profiles.attribute">
<ref name="k.profiles.attribute"/>
</define>
<define name="k.registry.arch.attribute">
<ref name="k.arch.attribute"/>
</define>
<define name="k.registry.source.attribute">
<attribute name="source">
<a:documentation>Name of registry source server</a:documentation>
</attribute>
</define>
<define name="k.registry.attlist">
<interleave>
<optional>
<ref name="k.registry.profiles.attribute"/>
</optional>
<optional>
<ref name="k.registry.arch.attribute"/>
</optional>
<ref name="k.registry.source.attribute"/>
</interleave>
</define>
<define name="k.registry">
<element name="registry">
<ref name="k.registry.attlist"/>
<oneOrMore>
<ref name="k.container"/>
</oneOrMore>
</element>
</define>
</div>
<!--
==========================================
common element <container>
-->
<div>
<define name="k.container.name.attribute">
<attribute name="name">
<a:documentation>Container name</a:documentation>
</attribute>
</define>
<define name="k.container.tag.attribute">
<attribute name="tag">
<a:documentation>Container tag, defaults to 'latest' if not specified</a:documentation>
</attribute>
</define>
<define name="k.container.path.attribute">
<attribute name="path">
<a:documentation>Container path, default to '/' if not specified</a:documentation>
</attribute>
</define>
<define name="k.container.fetch_only.attribute">
<attribute name="fetch_only">
<a:documentation>Only fetch the container but do not activate the
loading of the container at first boot</a:documentation>
<data type="boolean"/>
</attribute>
</define>
<define name="k.container.use_with.attribute">
<attribute name="use_with">
<a:documentation>Use container with specified container backend</a:documentation>
<choice>
<value>podman</value>
<value>docker</value>
</choice>
</attribute>
</define>
<define name="k.container.attlist">
<interleave>
<ref name="k.container.name.attribute"/>
<ref name="k.container.use_with.attribute"/>
<optional>
<ref name="k.container.tag.attribute"/>
</optional>
<optional>
<ref name="k.container.path.attribute"/>
</optional>
<optional>
<ref name="k.container.fetch_only.attribute"/>
</optional>
</interleave>
</define>
<define name="k.container">
<element name="container">
<ref name="k.container.attlist"/>
</element>
</define>
</div>
<!--
==========================================
common element <repository>
Expand Down
37 changes: 37 additions & 0 deletions kiwi/system/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from kiwi.utils.compress import Compress
from kiwi.utils.command_capabilities import CommandCapabilities
from kiwi.utils.rpm_database import RpmDataBase
from kiwi.builder.template.container_import import BuilderTemplateSystemdUnit
from kiwi.system.profile import Profile

from kiwi.exceptions import (
Expand Down Expand Up @@ -87,6 +88,42 @@ def __init__(self, xml_state: XMLState, root_dir: str):
self._preferences_lookup()
self._oemconfig_lookup()

def setup_registry_import(self) -> None:
"""
Fetch container(s) and activate systemd unit to load
fetched containers during boot
"""
for container in self.xml_state.get_containers():
log.info(f'Fetching container: {container.name}')
pathlib.Path(f'{self.root_dir}/{defaults.LOCAL_CONTAINERS}').mkdir(
parents=True, exist_ok=True
)
Command.run(
['chroot', self.root_dir] + container.fetch_command
)
if container.load_command:
log.info('--> Setup import unit')
service = BuilderTemplateSystemdUnit()
unit_template = service.get_container_import_template()
unit = unit_template.substitute(
{
'container_name': container.name,
'container_file': container.container_file,
'load_command': ' '.join(container.load_command)
}
)
unit_file = '{0}/usr/lib/systemd/system/{1}.service'.format(
self.root_dir, container.name
)
with open(unit_file, 'w') as systemd:
systemd.write(unit)
Command.run(
[
'chroot', self.root_dir,
'systemctl', 'enable', container.name
]
)

def import_description(self) -> None:
"""
Import XML descriptions, custom scripts, archives and
Expand Down
1 change: 1 addition & 0 deletions kiwi/tasks/system_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ def process(self):
setup.setup_timezone()
setup.setup_permissions()
setup.import_files()
setup.setup_registry_import()

# setup permanent image repositories after cleanup
setup.import_repositories_marked_as_imageinclude()
Expand Down
1 change: 1 addition & 0 deletions kiwi/tasks/system_prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ def process(self):
setup.setup_timezone()
setup.setup_permissions()
setup.import_files()
setup.setup_registry_import()

# setup permanent image repositories after cleanup
setup.import_repositories_marked_as_imageinclude()
Expand Down
Loading

0 comments on commit f7bf346

Please sign in to comment.