209 lines
6.4 KiB
Python
209 lines
6.4 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 collections
|
|
import os
|
|
|
|
# project
|
|
from ...command import Command
|
|
from ...utils.sync import DataSync
|
|
|
|
from ...exceptions import (
|
|
KiwiContainerSetupError
|
|
)
|
|
|
|
|
|
class ContainerSetupBase(object):
|
|
"""
|
|
Base class for setting up the root system to create
|
|
a container image from for e.g docker. The methods here
|
|
are generic to linux systems following the FHS standard
|
|
and modern enough e.g based on systemd
|
|
|
|
Attributes
|
|
|
|
* :attr:`root_dir`
|
|
root directory path name
|
|
|
|
* :attr:`custom_args`
|
|
dict of custom arguments
|
|
"""
|
|
def __init__(self, root_dir, custom_args=None):
|
|
if not os.path.exists(root_dir):
|
|
raise KiwiContainerSetupError(
|
|
'Container root directory %s does not exist' % root_dir
|
|
)
|
|
self.root_dir = root_dir
|
|
self.custom_args = {
|
|
'container_name': 'systemContainer'
|
|
}
|
|
self.post_init(custom_args)
|
|
|
|
def post_init(self, custom_args):
|
|
"""
|
|
Post initialization method
|
|
|
|
Implementation in specialized container setup class
|
|
|
|
:param list custom_args: unused
|
|
"""
|
|
pass
|
|
|
|
def setup(self):
|
|
"""
|
|
Setup container metadata
|
|
|
|
Implementation in specialized bootloader class required
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def create_fstab(self):
|
|
"""
|
|
Container boot mount setup
|
|
|
|
Initialize an empty fstab file, mount processes in a
|
|
container are controlled by the container infrastructure
|
|
"""
|
|
with open(self.root_dir + '/etc/fstab', 'w'):
|
|
pass
|
|
|
|
def deactivate_bootloader_setup(self):
|
|
"""
|
|
Container bootloader setup
|
|
|
|
Tell the system there is no bootloader configuration
|
|
it needs to care for. A container does not boot
|
|
"""
|
|
bootloader_setup = self.root_dir + '/etc/sysconfig/bootloader'
|
|
if os.path.exists(bootloader_setup):
|
|
self._update_config(
|
|
bootloader_setup,
|
|
{
|
|
'LOADER_LOCATION': 'LOADER_LOCATION="none"',
|
|
'LOADER_TYPE': 'LOADER_TYPE="none"'
|
|
}
|
|
)
|
|
|
|
def deactivate_root_filesystem_check(self):
|
|
"""
|
|
Container filesystem check setup
|
|
|
|
The root filesystem of a container could be an overlay
|
|
or a mapped device. In any case it should not be checked
|
|
for consistency as this is should be done by the container
|
|
infrastructure
|
|
"""
|
|
boot_setup = self.root_dir + '/etc/sysconfig/boot'
|
|
if os.path.exists(boot_setup):
|
|
self._update_config(
|
|
boot_setup,
|
|
{
|
|
'ROOTFS_BLKDEV': 'ROOTFS_BLKDEV="/dev/null"'
|
|
}
|
|
)
|
|
|
|
def deactivate_systemd_service(self, name):
|
|
"""
|
|
Container system services setup
|
|
|
|
Init systems among others also controls services which
|
|
starts at boot time. A container does not really boot.
|
|
Thus some services needs to be deactivated
|
|
|
|
:param string name: systemd service name
|
|
"""
|
|
service_file = self.root_dir + '/usr/lib/systemd/system/' + name
|
|
if os.path.exists(service_file):
|
|
try:
|
|
Command.run(
|
|
['ln', '-s', '-f', '/dev/null', service_file]
|
|
)
|
|
except Exception as e:
|
|
raise KiwiContainerSetupError(
|
|
'Failed to deactivate service %s: %s' %
|
|
(name, format(e))
|
|
)
|
|
|
|
def setup_root_console(self):
|
|
"""
|
|
Container console setup
|
|
|
|
/dev/console should be allowed to login by root
|
|
"""
|
|
securetty = self.root_dir + '/etc/securetty'
|
|
if not os.path.exists(securetty):
|
|
with open(securetty, 'w'):
|
|
pass
|
|
self._update_config(
|
|
securetty,
|
|
{
|
|
'console': 'console'
|
|
}
|
|
)
|
|
|
|
def setup_static_device_nodes(self):
|
|
"""
|
|
Container device node setup
|
|
|
|
Without subsystems like udev running in a container it is
|
|
required to provide a set of device nodes to let the
|
|
system in the container function correctly. This is
|
|
done by syncing the host system nodes to the container.
|
|
That this will also create device nodes which are not
|
|
necessarily present in the container later is a know
|
|
limitation of this method and considered harmless
|
|
"""
|
|
try:
|
|
data = DataSync('/dev/', self.root_dir + '/dev/')
|
|
data.sync_data(
|
|
options=['-z', '-a', '-x', '--devices', '--specials']
|
|
)
|
|
except Exception as e:
|
|
raise KiwiContainerSetupError(
|
|
'Failed to create static container nodes %s' % format(e)
|
|
)
|
|
|
|
def get_container_name(self):
|
|
"""
|
|
Container name
|
|
|
|
:return: name
|
|
:rtype: string
|
|
"""
|
|
return self.custom_args['container_name']
|
|
|
|
def _update_config(self, filename, update_record):
|
|
data = []
|
|
with open(filename, 'r') as config:
|
|
data = config.read().rsplit('\n')
|
|
|
|
sorted_record = collections.OrderedDict(
|
|
sorted(update_record.items())
|
|
)
|
|
for current_value, new_value in list(sorted_record.items()):
|
|
entry_found = False
|
|
for index in range(0, len(data)):
|
|
line = data[index]
|
|
if line.startswith(current_value):
|
|
entry_found = True
|
|
data[index] = new_value
|
|
if not entry_found:
|
|
data.append(new_value)
|
|
|
|
with open(filename, 'w') as config:
|
|
config.write('%s\n' % '\n'.join(data))
|