Refactor container factory classes

This commit refactors the container related classes to turn them into
proper factory classes and also includes type hints to facilitate
it's use from an API POV. Related to #1498
This commit is contained in:
David Cassany 2020-12-15 12:38:33 +01:00 committed by Marcus Schäfer
parent 998d473bc0
commit 99be52baec
No known key found for this signature in database
GPG Key ID: AD11DD02B44996EF
12 changed files with 118 additions and 81 deletions

View File

@ -113,7 +113,7 @@ class ContainerBuilder:
log.info(
'Setting up %s container', self.requested_container_type
)
container_setup = ContainerSetup(
container_setup = ContainerSetup.new(
self.requested_container_type, self.root_dir,
self.container_config
)
@ -130,7 +130,7 @@ class ContainerBuilder:
log.info(
'--> Creating container image'
)
container_image = ContainerImage(
container_image = ContainerImage.new(
self.requested_container_type, self.root_dir, self.container_config
)
self.filename = container_image.create(

View File

@ -15,16 +15,18 @@
# You should have received a copy of the GNU General Public License
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
#
# project
from kiwi.container.oci import ContainerImageOCI
from kiwi.container.appx import ContainerImageAppx
import importlib
from abc import (
ABCMeta,
abstractmethod
)
from kiwi.exceptions import (
KiwiContainerImageSetupError
)
class ContainerImage:
class ContainerImage(metaclass=ABCMeta):
"""
**Container Image factory**
@ -32,20 +34,29 @@ class ContainerImage:
:param string root_dir: root directory path name
:param dict custom_args: custom arguments
"""
def __new__(self, name, root_dir, custom_args=None):
if name == 'docker':
return ContainerImageOCI(
root_dir, 'docker-archive', custom_args=custom_args
@abstractmethod
def __init__(self) -> None:
return None # pragma: no cover
@staticmethod
def new(name: str, root_dir: str, custom_args: dict = None):
name_map = {
'docker': 'OCI',
'oci': 'OCI',
'appx': 'Appx'
}
args_map = {
'docker': [root_dir, 'docker-archive', custom_args],
'oci': [root_dir, 'oci-archive', custom_args],
'appx': [root_dir, custom_args]
}
try:
container_image = importlib.import_module(
'kiwi.container.{}'.format('oci' if name == 'docker' else name)
)
elif name == 'oci':
return ContainerImageOCI(
root_dir, 'oci-archive', custom_args=custom_args
)
elif name == 'appx':
return ContainerImageAppx(
root_dir, custom_args=custom_args
)
else:
module_name = 'ContainerImage{}'.format(name_map[name])
return container_image.__dict__[module_name](*args_map[name])
except Exception:
raise KiwiContainerImageSetupError(
'Support for {0} container not implemented'.format(name)
)

View File

@ -118,7 +118,7 @@ class ContainerImageOCI:
exclude_list.append('sys/*')
exclude_list.append('proc/*')
oci = OCI()
oci = OCI.new()
if base_image:
oci.import_container_image(
'oci-archive:{0}:{1}'.format(

View File

@ -15,28 +15,40 @@
# You should have received a copy of the GNU General Public License
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
#
# project
from kiwi.container.setup.docker import ContainerSetupDocker
from kiwi.container.setup.oci import ContainerSetupOCI
from kiwi.container.setup.appx import ContainerSetupAppx
import importlib
from abc import (
ABCMeta,
abstractmethod
)
# project
from kiwi.exceptions import (
KiwiContainerSetupError
)
class ContainerSetup:
class ContainerSetup(metaclass=ABCMeta):
"""
container setup factory
"""
def __new__(self, name, root_dir, custom_args=None):
if name == 'docker':
return ContainerSetupDocker(root_dir, custom_args)
elif name == 'oci':
return ContainerSetupOCI(root_dir, custom_args)
elif name == 'appx':
return ContainerSetupAppx(root_dir, custom_args)
else:
@abstractmethod
def __init__(self) -> None:
return None # pragma: no cover
@staticmethod
def new(name: str, root_dir: str, custom_args: dict = None):
name_map = {
'docker': 'Docker',
'oci': 'OCI',
'appx': 'Appx'
}
try:
container_setup = importlib.import_module(
'kiwi.container.setup.{}'.format(name)
)
module_name = 'ContainerSetup{}'.format(name_map[name])
return container_setup.__dict__[module_name](root_dir, custom_args)
except Exception:
raise KiwiContainerSetupError(
'Support for {0} container not implemented'.format(name)
)

View File

@ -15,28 +15,42 @@
# You should have received a copy of the GNU General Public License
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
#
# project
from kiwi.oci_tools.umoci import OCIUmoci
from kiwi.oci_tools.buildah import OCIBuildah
from kiwi.runtime_config import RuntimeConfig
import importlib
from abc import (
ABCMeta,
abstractmethod
)
# project
from kiwi.runtime_config import RuntimeConfig
from kiwi.exceptions import (
KiwiOCIArchiveToolError
)
class OCI:
class OCI(metaclass=ABCMeta):
"""
**OCI Factory**
"""
def __new__(self):
@abstractmethod
def __init__(self) -> None:
return None # pragma: no cover
@staticmethod
def new():
name_map = {
'umoci': 'Umoci',
'buildah': 'Buildah'
}
runtime_config = RuntimeConfig()
tool_name = runtime_config.get_oci_archive_tool()
if tool_name == 'umoci':
return OCIUmoci()
elif tool_name == 'buildah':
return OCIBuildah()
else:
try:
oci_tool = importlib.import_module(
'kiwi.oci_tools.{}'.format(tool_name)
)
module_name = 'OCI{}'.format(name_map[tool_name])
return oci_tool.__dict__[module_name]()
except Exception:
raise KiwiOCIArchiveToolError(
'No support for {0} tool available'.format(tool_name)
)

View File

@ -55,7 +55,7 @@ class RootImportOCI(RootImportBase):
log.warning('Bypassing base image URI to OCI tools')
image_uri = self.unknown_uri
oci = OCI()
oci = OCI.new()
oci.import_container_image(image_uri)
oci.unpack()
oci.import_rootfs(self.root_dir)

View File

@ -112,22 +112,22 @@ class TestContainerBuilder:
@patch('kiwi.builder.container.ContainerImage')
def test_create(self, mock_image, mock_setup):
container_setup = mock.Mock()
mock_setup.return_value = container_setup
mock_setup.new.return_value = container_setup
container_image = mock.Mock()
container_image.create = mock.Mock(
return_value='target_dir/image_name.x86_64-1.2.3.docker.tar.xz'
)
mock_image.return_value = container_image
mock_image.new.return_value = container_image
self.setup.export_package_verification.return_value = '.verified'
self.setup.export_package_changes.return_value = '.changes'
self.setup.export_package_list.return_value = '.packages'
self.container.base_image = None
self.container.create()
mock_setup.assert_called_once_with(
mock_setup.new.assert_called_once_with(
'docker', 'root_dir', self.container_config
)
container_setup.setup.assert_called_once_with()
mock_image.assert_called_once_with(
mock_image.new.assert_called_once_with(
'docker', 'root_dir', self.container_config
)
container_image.create.assert_called_once_with(
@ -188,7 +188,7 @@ class TestContainerBuilder:
container_image.create = mock.Mock(
return_value='target_dir/image_name.x86_64-1.2.3.docker.tar.xz'
)
mock_image.return_value = container_image
mock_image.new.return_value = container_image
container = ContainerBuilder(
self.xml_state, 'target_dir', 'root_dir'
@ -212,7 +212,7 @@ class TestContainerBuilder:
'checksumvalue', 'root_dir/image/imported_root.md5'
)
mock_image.assert_called_once_with(
mock_image.new.assert_called_once_with(
'docker', 'root_dir', self.container_config
)
container_image.create.assert_called_once_with(

View File

@ -9,25 +9,25 @@ from kiwi.exceptions import KiwiContainerImageSetupError
class TestContainerImage:
def test_container_image_not_implemented(self):
with raises(KiwiContainerImageSetupError):
ContainerImage('foo', 'root_dir')
ContainerImage.new('foo', 'root_dir')
@patch('kiwi.container.ContainerImageOCI')
@patch('kiwi.container.oci.ContainerImageOCI')
def test_container_image_docker(self, mock_docker):
ContainerImage('docker', 'root_dir')
ContainerImage.new('docker', 'root_dir')
mock_docker.assert_called_once_with(
'root_dir', 'docker-archive', custom_args=None
'root_dir', 'docker-archive', None
)
@patch('kiwi.container.ContainerImageOCI')
@patch('kiwi.container.oci.ContainerImageOCI')
def test_container_image_oci(self, mock_oci):
ContainerImage('oci', 'root_dir')
ContainerImage.new('oci', 'root_dir')
mock_oci.assert_called_once_with(
'root_dir', 'oci-archive', custom_args=None
'root_dir', 'oci-archive', None
)
@patch('kiwi.container.ContainerImageAppx')
@patch('kiwi.container.appx.ContainerImageAppx')
def test_container_image_appx(self, mock_appx):
ContainerImage('appx', 'root_dir')
ContainerImage.new('appx', 'root_dir')
mock_appx.assert_called_once_with(
'root_dir', custom_args=None
'root_dir', None
)

View File

@ -120,7 +120,7 @@ class TestContainerImageOCI:
def test_create_oci_archive(self, mock_cache, mock_OCI):
mock_cache.return_value = 'var/cache/kiwi'
mock_oci = mock.Mock()
mock_OCI.return_value = mock_oci
mock_OCI.new.return_value = mock_oci
self.oci.runtime_config.get_container_compression = mock.Mock(
return_value=None
@ -164,7 +164,7 @@ class TestContainerImageOCI:
):
mock_cache.return_value = 'var/cache/kiwi'
mock_oci = mock.Mock()
mock_OCI.return_value = mock_oci
mock_OCI.new.return_value = mock_oci
self.oci.create('result.tar', 'root_dir/image/image_file')

View File

@ -9,19 +9,19 @@ from kiwi.exceptions import KiwiContainerSetupError
class TestContainerSetup:
def test_container_not_implemented(self):
with raises(KiwiContainerSetupError):
ContainerSetup('foo', 'root_dir')
ContainerSetup.new('foo', 'root_dir')
@patch('kiwi.container.setup.ContainerSetupDocker')
@patch('kiwi.container.setup.docker.ContainerSetupDocker')
def test_container_docker(self, mock_docker):
ContainerSetup('docker', 'root_dir')
ContainerSetup.new('docker', 'root_dir')
mock_docker.assert_called_once_with('root_dir', None)
@patch('kiwi.container.setup.ContainerSetupOCI')
@patch('kiwi.container.setup.oci.ContainerSetupOCI')
def test_container_oci(self, mock_oci):
ContainerSetup('oci', 'root_dir')
ContainerSetup.new('oci', 'root_dir')
mock_oci.assert_called_once_with('root_dir', None)
@patch('kiwi.container.setup.ContainerSetupAppx')
@patch('kiwi.container.setup.appx.ContainerSetupAppx')
def test_container_appx(self, mock_appx):
ContainerSetup('appx', 'root_dir')
ContainerSetup.new('appx', 'root_dir')
mock_appx.assert_called_once_with('root_dir', None)

View File

@ -15,24 +15,24 @@ class TestOCI:
self.runtime_config = Mock()
self.runtime_config.get_oci_archive_tool = Mock()
@patch('kiwi.oci_tools.OCIUmoci')
@patch('kiwi.oci_tools.umoci.OCIUmoci')
@patch('kiwi.oci_tools.RuntimeConfig')
def test_oci_tool_umoci(
self, mock_RuntimeConfig, mock_OCIUmoci
):
self.runtime_config.get_oci_archive_tool.return_value = 'umoci'
mock_RuntimeConfig.return_value = self.runtime_config
OCI()
OCI.new()
mock_OCIUmoci.assert_called_once_with()
@patch('kiwi.oci_tools.OCIBuildah')
@patch('kiwi.oci_tools.buildah.OCIBuildah')
@patch('kiwi.oci_tools.RuntimeConfig')
def test_oci_tool_buildah(
self, mock_RuntimeConfig, mock_OCIBuildah
):
self.runtime_config.get_oci_archive_tool.return_value = 'buildah'
mock_RuntimeConfig.return_value = self.runtime_config
OCI()
OCI.new()
mock_OCIBuildah.assert_called_once_with()
@patch('kiwi.oci_tools.RuntimeConfig')
@ -40,4 +40,4 @@ class TestOCI:
self.runtime_config.get_oci_archive_tool.return_value = 'foo'
mock_RuntimeConfig.return_value = self.runtime_config
with raises(KiwiOCIArchiveToolError):
OCI()
OCI.new()

View File

@ -42,7 +42,7 @@ class TestRootImportOCI:
@patch('kiwi.system.root_import.oci.OCI')
def test_sync_data(self, mock_OCI, mock_path, mock_md5, mock_compress):
oci = Mock()
mock_OCI.return_value = oci
mock_OCI.new.return_value = oci
md5 = Mock()
mock_md5.return_value = Mock()
@ -52,7 +52,7 @@ class TestRootImportOCI:
self.oci_import.sync_data()
mock_OCI.assert_called_once_with()
mock_OCI.new.assert_called_once_with()
oci.unpack.assert_called_once_with()
oci.import_rootfs.assert_called_once_with(
@ -70,7 +70,7 @@ class TestRootImportOCI:
self, mock_OCI, mock_path, mock_md5, mock_compress
):
oci = Mock()
mock_OCI.return_value = oci
mock_OCI.new.return_value = oci
md5 = Mock()
mock_md5.return_value = Mock()
@ -80,7 +80,7 @@ class TestRootImportOCI:
self.oci_import.sync_data()
mock_OCI.assert_called_once_with()
mock_OCI.new.assert_called_once_with()
oci.unpack.assert_called_once_with()
oci.import_rootfs.assert_called_once_with(
@ -100,7 +100,7 @@ class TestRootImportOCI:
):
mock_exists.return_value = True
oci = Mock()
mock_OCI.return_value = oci
mock_OCI.new.return_value = oci
md5 = Mock()
mock_md5.return_value = Mock()
with patch.dict('os.environ', {'HOME': '../data'}):
@ -111,7 +111,7 @@ class TestRootImportOCI:
with self._caplog.at_level(logging.WARNING):
oci_import.sync_data()
mock_OCI.assert_called_once_with()
mock_OCI.new.assert_called_once_with()
oci.import_container_image.assert_called_once_with(
'docker:image:tag'
)