kiwi-el8/kiwi/system/root_import/base.py
Marcus Schäfer a7b984115d
Fixed delta_root build
The support for delta_root allows to build a delta container
image from a given base container. Due to the refactoring of
the kiwi code base using context managers no explicit deletion
of instances happens anymore. This uncovered a weakness of
the delta root code at the level of the overlay mount. At
the time of the umount there are still active temporary
mount handlers which keeps the mountpoint busy. In order to
fix this properly also the PackageManager factory is now
a context manager and the Repository factory received a
cleanup method which is called when the PackageManager goes
out of scope. This refactoring also fixes the busy state
when building deltas
2024-03-07 15:50:58 +01:00

171 lines
5.8 KiB
Python

# Copyright (c) 2015 SUSE Linux 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
import logging
# project
from kiwi.xml_state import XMLState
from kiwi.defaults import Defaults
from kiwi.system.setup import SystemSetup
from kiwi.path import Path
from kiwi.command import Command
from kiwi.utils.checksum import Checksum
from kiwi.exceptions import (
KiwiRootImportError,
KiwiUriTypeUnknown
)
log = logging.getLogger('kiwi')
class RootImportBase:
"""
Imports the root system from an already packed image.
* :attr:`root_dir`
root directory path name
* :attr:`image_uri`
Uri object to store source location
* :attr:`custom_args`
Dictonary to set specialized class specific configuration values
"""
def __init__(self, root_dir, image_uri, custom_args=None):
self.unknown_uri = None
self.overlay = None
self.root_dir = root_dir
try:
if image_uri.is_remote():
raise KiwiRootImportError(
'Only local imports are supported'
)
self.image_file = image_uri.translate()
if not os.path.exists(self.image_file):
raise KiwiRootImportError(
'Could not stat base image file: {0}'.format(
self.image_file
)
)
except KiwiUriTypeUnknown:
# Let specialized class handle unknown uri schemes
log.warning(
'Unkown URI type for the base image: %s', image_uri.uri
)
self.unknown_uri = image_uri.uri
finally:
self.post_init(custom_args)
def post_init(self, custom_args):
"""
Initialization of the specialized import class
Implementation in specialized root import class
"""
pass
def overlay_finalize(self, xml_state: XMLState) -> None:
"""
Umount the overlay root, delete lower and work directories
and move the upper (delta) to represent the final root_dir.
All files that got deleted will be reported in a metadata
file named Defaults.get_removed_files_name(). This information
can be used by other tools to know about actively deleted
files and bring them back at a later point in time.
:param XMLState xml_state: Instance of XML state
"""
if self.overlay:
pinch_reference = f'{self.root_dir}_cow_before_pinch'
removed = f'{self.root_dir}/{Defaults.get_removed_files_name()}'
system = SystemSetup(xml_state, self.root_dir)
# Run config-overlay.sh
system.call_config_overlay_script()
# Run config-host-overlay.sh
system.call_config_host_overlay_script(
working_directory=self.root_dir
)
# Umount and rename upper to be the new root
self.overlay.umount()
Path.wipe(self.root_dir)
Path.rename(self.overlay.upper, self.root_dir)
# Create removed files metadata
if not os.path.isdir(pinch_reference):
Path.create(pinch_reference)
exclude_options = []
removed_files = []
for item in Defaults.get_exclude_list_for_removed_files_detection():
exclude_options.append('--exclude')
exclude_options.append(item)
get_removed = [
'rsync', '-av', '--dry-run', '--out-format=%n'
] + exclude_options + [
f'{pinch_reference}/', f'{self.root_dir}/'
]
for item in Command.run(get_removed).output.split(os.linesep):
entry = f'{pinch_reference}/{item}'
if item and os.path.exists(entry) and not os.path.isdir(entry):
removed_files.append(item)
Path.wipe(pinch_reference)
if removed_files:
with open(removed, 'w') as removed_fd:
for filename in removed_files:
removed_fd.write(filename)
removed_fd.write(os.linesep)
# delete character device nodes from delta tree.
# (c) device nodes are used to track deleted files
# and directories from the lower path of the overlayfs
# tree. Since we extract the upper path of the overlay
# any changes to the lower tree cannot be tracked.
Command.run(
[
'find', self.root_dir, '-type', 'c', '-delete'
]
)
Path.wipe(self.overlay.lower)
Path.wipe(self.overlay.work)
def overlay_data(self):
"""
Synchronize data from the given base image to the target root
directory as an overlayfs mounted target.
Implementation in specialized root import class
"""
raise NotImplementedError
def sync_data(self):
"""
Synchronizes the root system of `image_file` into the root_dir
Implementation in specialized root import class
"""
raise NotImplementedError
def _make_checksum(self, image):
checksum = Checksum(image)
checksum.md5(''.join([image, '.md5']))