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
171 lines
5.8 KiB
Python
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']))
|