Implement locale filter in minimize_size plugin

This filter is rather aggressive, since it also hooks into the
bootstrapping process itself to prevent debootstrap from unpacking
specific locale files
This commit is contained in:
Anders Ingemann 2015-12-11 00:50:26 +01:00
parent 42d12d440d
commit 6ae859f886
5 changed files with 127 additions and 1 deletions

View file

@ -29,7 +29,13 @@ def resolve_tasks(taskset, manifest):
taskset.add(tasks.AptGzipIndexes) taskset.add(tasks.AptGzipIndexes)
if apt.get('autoremove_suggests', False): if apt.get('autoremove_suggests', False):
taskset.add(tasks.AptAutoremoveSuggests) taskset.add(tasks.AptAutoremoveSuggests)
if 'locales' in apt:
taskset.update([tasks.CreateBootstrapFilterScripts,
tasks.DeleteBootstrapFilterScripts,
tasks.FilterLocales,
])
def resolve_rollback_tasks(taskset, manifest, completed, counter_task): def resolve_rollback_tasks(taskset, manifest, completed, counter_task):
counter_task(taskset, tasks.AddFolderMounts, tasks.RemoveFolderMounts) counter_task(taskset, tasks.AddFolderMounts, tasks.RemoveFolderMounts)
counter_task(taskset, tasks.CreateBootstrapFilterScripts, tasks.DeleteBootstrapFilterScripts)

View file

@ -0,0 +1,2 @@
#!/bin/sh
grep 'EXCLUDE_PATTERN' | grep --invert-match --fixed-strings 'INCLUDE_PATHS'

View file

@ -0,0 +1,26 @@
#!/bin/sh
# This script does not override anything defined in /usr/share/debootstrap/scripts
# Instead we use it to redefine extract_dpkg_deb_data(), so that we may exclude
# certain files during bootstrapping.
extract_dpkg_deb_data () {
local pkg="$1"
local excludes_file="/tmp/bootstrap-vz-excludes-$$"
# List all files in $pkg and run them through the filter (avoid exit status >0 if no matches are found)
dpkg-deb --fsys-tarfile "$pkg" | tar -t | BOOTSTRAP_FILES_FILTER_PATH > "$excludes_file" || true
dpkg-deb --fsys-tarfile "$pkg" | tar --exclude-from "$excludes_file" -xf -
rm "$excludes_file"
}
# Direct copypasta from the debootstrap script where it determines
# which script to run. We do exactly the same but leave out the
# if [ "$4" != "" ] part so that we can source the script that
# should've been sourced in this scripts place.
SCRIPT="$DEBOOTSTRAP_DIR/scripts/$SUITE"
if [ -n "$VARIANT" ] && [ -e "${SCRIPT}.${VARIANT}" ]; then
SCRIPT="${SCRIPT}.${VARIANT}"
fi
. $SCRIPT

View file

@ -6,6 +6,7 @@ from bootstrapvz.common.tasks import filesystem
from bootstrapvz.common.tasks import host from bootstrapvz.common.tasks import host
from bootstrapvz.common.tasks import partitioning from bootstrapvz.common.tasks import partitioning
from bootstrapvz.common.tasks import volume from bootstrapvz.common.tasks import volume
from bootstrapvz.common.tasks import workspace
from bootstrapvz.common.tools import sed_i from bootstrapvz.common.tools import sed_i
from bootstrapvz.common.tools import log_check_call from bootstrapvz.common.tools import log_check_call
import os import os
@ -83,12 +84,100 @@ class ShrinkVolume(Task):
@classmethod @classmethod
def run(cls, info): def run(cls, info):
from bootstrapvz.common.tools import log_check_call
perm = os.stat(info.volume.image_path).st_mode & 0777 perm = os.stat(info.volume.image_path).st_mode & 0777
log_check_call(['/usr/bin/vmware-vdiskmanager', '-k', info.volume.image_path]) log_check_call(['/usr/bin/vmware-vdiskmanager', '-k', info.volume.image_path])
os.chmod(info.volume.image_path, perm) os.chmod(info.volume.image_path, perm)
class CreateBootstrapFilterScripts(Task):
description = 'Creating the bootstrapping locales filter script'
phase = phases.os_installation
successors = [bootstrap.Bootstrap]
# Inspired by:
# https://github.com/docker/docker/blob/1d775a54cc67e27f755c7338c3ee938498e845d7/contrib/mkimage/debootstrap
@classmethod
def run(cls, info):
if info.bootstrap_script is not None:
from bootstrapvz.common.exceptions import TaskError
raise TaskError('info.bootstrap_script seems to already be set '
'and is conflicting with this task')
bootstrap_script = os.path.join(info.workspace, 'bootstrap_script.sh')
filter_script = os.path.join(info.workspace, 'bootstrap_files_filter.sh')
shutil.copy(os.path.join(assets, 'bootstrap-script.sh'), bootstrap_script)
shutil.copy(os.path.join(assets, 'bootstrap-files-filter.sh'), filter_script)
sed_i(bootstrap_script, r'BOOTSTRAP_FILES_FILTER_PATH', filter_script)
sed_i(filter_script, r'EXCLUDE_PATTERN', "./usr/share/locale/.\+\|./usr/share/man/.\+")
keep_files = ['./usr/share/locale/locale.alias',
'./usr/share/man/man1',
'./usr/share/man/man2',
'./usr/share/man/man3',
'./usr/share/man/man4',
'./usr/share/man/man5',
'./usr/share/man/man6',
'./usr/share/man/man7',
'./usr/share/man/man8',
'./usr/share/man/man9',
]
locales = info.manifest.plugins['minimize_size']['apt']['locales']
keep_files.extend(map(lambda l: './usr/share/locale/' + l + '/', locales))
keep_files.extend(map(lambda l: './usr/share/man/' + l + '/', locales))
sed_i(filter_script, r'INCLUDE_PATHS', "\n".join(keep_files))
os.chmod(filter_script, 0755)
info.bootstrap_script = bootstrap_script
info._minimize_size['filter_script'] = filter_script
class DeleteBootstrapFilterScripts(Task):
description = 'Deleting the bootstrapping locales filter script'
phase = phases.cleaning
successors = [workspace.DeleteWorkspace]
@classmethod
def run(cls, info):
os.remove(info._minimize_size['filter_script'])
del info._minimize_size['filter_script']
os.remove(info.bootstrap_script)
class FilterLocales(Task):
description = 'Configuring dpkg to only include specific locales/manpages when installing packages'
phase = phases.os_installation
successors = [bootstrap.Bootstrap]
# Snatched from:
# https://github.com/docker/docker/blob/1d775a54cc67e27f755c7338c3ee938498e845d7/contrib/mkimage/debootstrap
# and
# https://raphaelhertzog.com/2010/11/15/save-disk-space-by-excluding-useless-files-with-dpkg/
@classmethod
def run(cls, info):
# This is before we start bootstrapping, so we create dpkg.cfg.d manually
os.makedirs(os.path.join(info.root, 'etc/dpkg/dpkg.cfg.d'))
locale_lines = ['path-exclude=/usr/share/locale/*',
'path-include=/usr/share/locale/locale.alias']
manpages_lines = ['path-exclude=/usr/share/man/*',
'path-include=/usr/share/man/man[1-9]']
locales = info.manifest.plugins['minimize_size']['apt']['locales']
locale_lines.extend(map(lambda l: 'path-include=/usr/share/locale/' + l + '/*', locales))
manpages_lines.extend(map(lambda l: 'path-include=/usr/share/man/' + l + '/*', locales))
locales_path = os.path.join(info.root, 'etc/dpkg/dpkg.cfg.d/10filter-locales')
manpages_path = os.path.join(info.root, 'etc/dpkg/dpkg.cfg.d/10filter-manpages')
with open(locales_path, 'w') as locale_filter:
locale_filter.write('\n'.join(locale_lines) + '\n')
with open(manpages_path, 'w') as manpages_filter:
manpages_filter.write('\n'.join(manpages_lines) + '\n')
class AutomateAptClean(Task): class AutomateAptClean(Task):
description = 'Configuring apt to always clean everything out when it\'s done' description = 'Configuring apt to always clean everything out when it\'s done'
phase = phases.package_installation phase = phases.package_installation

View file

@ -31,3 +31,6 @@ plugins:
languages: [none] languages: [none]
gzip_indexes: true gzip_indexes: true
autoremove_suggests: true autoremove_suggests: true
locales:
- en
- en_US