# 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 kiwi.command import Command
from kiwi.exceptions import (
KiwiContainerSetupError
)
[docs]class ContainerSetupBase:
"""
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 = custom_args or {}
if 'container_name' not in self.custom_args:
self.custom_args['container_name'] = 'system-container'
self.post_init(custom_args)
[docs] def post_init(self, custom_args):
"""
Post initialization method
Implementation in specialized container setup class
:param list custom_args: unused
"""
pass
[docs] def setup(self):
"""
Setup container metadata
Implementation in specialized bootloader class required
"""
raise NotImplementedError
[docs] 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"'
}
)
[docs] 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"'
}
)
[docs] 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))
)
[docs] 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'
}
)
[docs] def get_container_name(self):
"""
Container name
:return: name
:rtype: str
"""
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))