-
Notifications
You must be signed in to change notification settings - Fork 51
/
build_sysext
executable file
·313 lines (257 loc) · 12.7 KB
/
build_sysext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
#!/bin/bash
# Copyright (c) 2023 The Flatcar Maintainers.
# Use of this source code is governed by a BSD-style license that can
# be found in the LICENSE file.
#
# Script to generate sysext. See systemd-sysext(8). Prerequisite is
# that you've run build_packages and build_image.
SCRIPT_ROOT=$(dirname "$(readlink -f "$0")")
. "${SCRIPT_ROOT}/common.sh" || exit 1
# Script must run inside the chroot
assert_inside_chroot
assert_root_user
default_imagedir="$(readlink -f "${SCRIPT_ROOT}/../build/images")/<BOARD>/latest/"
# All these are used to set up the 'BUILD_DIR' variable
DEFINE_string board "${DEFAULT_BOARD}" \
"The board to build a sysext for."
DEFINE_string metapkgs '' \
"Comma-separated list of meta-packages to build from source and install into sysext image."
DEFINE_string squashfs_base '' \
"The path to the squashfs base image. Defaults to the most current image built in '${default_imagedir}/${FLATCAR_PRODUCTION_IMAGE_SYSEXT_BASE}'."
DEFINE_string image_builddir '' \
"Custom directory to build the sysext in. Defaults to a 'sysext' sub-directory of the directory the squashfs base image resides in; '${default_imagedir}/sysext' by default."
DEFINE_boolean strip_binaries "${FLAGS_FALSE}" \
"After installation, scan sysext root for unstripped binaries and strip these. WARNING - this can subtly break some packages, e.g. Docker (see https://github.com/moby/moby/blob/master/project/PACKAGERS.md#stripping-binaries)."
DEFINE_string manglefs_script '' \
"A path to executable that will customize the rootfs of the sysext image."
DEFINE_boolean generate_pkginfo "${FLAGS_FALSE}" \
"Generate an additional squashfs '<sysext_name>_pkginfo.raw' with portage package meta-information (/var/db ...). Useful for creating sysext dependencies; see 'base_pkginfo' below."
DEFINE_string base_pkginfo "" \
"Colon-separated list of pkginfo squashfs paths / files generated via 'generate_pkginfo' to base this sysext on. The corresponding base sysexts are expected to be merged with the sysext generated."
DEFINE_string compression "zstd" \
"Compression to use for sysext squashfs. One of 'gzip', 'lzo', 'lz4', 'xz', or 'zstd'. Must be supported by the Flatcar squashfs kernel module in order for the sysext to work."
DEFINE_string mksquashfs_opts "" \
"Additional command line options to pass to mksquashfs. See 'man 1 mksquashfs'. If <compression> is 'zstd' (the default), this option defaults to '-Xcompression-level 22 -b 512K'. Otherwise the default is empty."
DEFINE_boolean ignore_version_mismatch "${FLAGS_FALSE}" \
"Ignore version mismatch between SDK board packages and base squashfs. DANGEROUS."
FLAGS_HELP="USAGE: build_sysext [flags] <sysext_name> <binary_package> [<binary_package> ...]
This script is used to build a Flatcar sysext image.
The sysext will be based on an OS image build's sysext base squashfs, i.e. it is specific to
a Flatcar build or release.
The base squashfs can either come from a local build or downloaded from an official release.
By default, the sysext will be built in a 'sysext' sub-dir of the directory the squashfs base image
is in, but this can be changed with the --image_builddir option.
Examples:
Builds a sysext image named 'interpreters' with 'dev-lang/python' and 'dev-lang/perl' packages for the
most recent production image (default architecture, likely amd64) in the defaut build directory:
sudo build_sysext \\
interpreters dev-lang/python dev-lang/perl
Builds a sysext image named 'oem-azure' in the 'oem-images' sub-directory with
metapackage 'coreos-base/oem-azure' for the arm64 squashfs base at
'build/artifacts/flatcar_production_image_sysext.squashfs':
sudo build_sysext \\
--board=arm64-usr \\
--metapkgs=coreos-base/oem-azure \\
--mangle_fs=sdk_container/src/third_party/coreos-overlay/coreos-base/oem-azure/files/manglefs.sh \\
--squashfs_base=build/artifacts/flatcar_production_image_sysext.squashfs \\
--image_builddir=oem-images \\
oem-azure
Mandatory command line parameters:
<sysext_name> - name of the sysext output file.
<binary_package> - List of existing binary packages to install. Can be omitted if --metapkgs was specified.
"
show_help_if_requested "$@"
# Parse command line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
# Only now can we die on error. shflags functions leak non-zero error codes,
# so will die prematurely if 'switch_to_strict_mode' is specified before now.
switch_to_strict_mode -uo pipefail
# Validate command line parameters
SYSEXTNAME="${1:-}"
if [[ -z "${SYSEXTNAME}" ]]; then
die "No sysext name provided."
fi
shift
if [[ -z "$FLAGS_squashfs_base" ]] ; then
FLAGS_squashfs_base="$(readlink -f "${SCRIPT_ROOT}/../build/images/${FLAGS_board}/latest/${FLATCAR_PRODUCTION_IMAGE_SYSEXT_BASE}")"
fi
if [[ ! -f "${FLAGS_squashfs_base}" ]] ; then
die "Squashfs base '${FLAGS_squashfs_base}' not found."
fi
if [[ -z "${FLAGS_image_builddir}" ]]; then
FLAGS_image_builddir="$(dirname "${FLAGS_squashfs_base}")/sysext"
fi
BUILD_DIR=$(realpath "${FLAGS_image_builddir}")
mkdir -p "${BUILD_DIR}"
if [[ "${FLAGS_compression}" = "zstd" && -z "${FLAGS_mksquashfs_opts}" ]] ; then
FLAGS_mksquashfs_opts="-Xcompression-level 22 -b 512k"
fi
source "${BUILD_LIBRARY_DIR}/toolchain_util.sh" || exit 1
source "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1
source "${BUILD_LIBRARY_DIR}/reports_util.sh" || exit 1
# Architecture values are taken from systemd.unit(5).
declare -A SYSEXT_ARCHES
SYSEXT_ARCHES['amd64-usr']='x86-64'
SYSEXT_ARCHES['arm64-usr']='arm64'
declare -r SYSEXT_ARCHES
# Usage: _get_sysext_arch board [board...]
_get_sysext_arch() {
local board=${1}
if [[ ${#SYSEXT_ARCHES["${board}"]} -ne 0 ]]; then
echo "${SYSEXT_ARCHES["${board}"]}"
else
die "Unknown board '${board}'"
fi
}
cleanup() {
local dirs=(
"${BUILD_DIR}/fs-root"
"${BUILD_DIR}/install-root"
"${BUILD_DIR}/workdir"
"${BUILD_DIR}/img-rootfs"
)
umount "${dirs[@]}" 2>/dev/null || true
rm -rf "${dirs[@]}" || true
if [[ -d "${BUILD_DIR}/base-pkginfo" ]] ; then
umount "${BUILD_DIR}/base-pkginfo"/* 2>/dev/null || true
rm -rf "${BUILD_DIR}/base-pkginfo" || true
fi
rm -rf "${BUILD_DIR}/img-pkginfo"
}
# Set up trap to execute cleanup() on script exit
trap cleanup EXIT
ARCH=$(_get_sysext_arch "${FLAGS_board}")
cleanup
# If we need to handle pkginfo squashfs files, create mount points under
# ${BUILD_DIR}/base-pkginfo, mount the squashfs images, and add the mount paths to
# the list of lowerdirs.
pkginfo_lowerdirs=""
if [[ -n "${FLAGS_base_pkginfo}" ]] ; then
for entry in $(echo ${FLAGS_base_pkginfo} | sed 's/:/ /g'); do
ppath="$(readlink -f "${entry}")"
if [[ ! -f "${ppath}" ]] ; then
error "--base_pkginfo contains invalid entries."
error "Pkginfo file '${ppath}' does not exist."
die "Full --base_pkginfo: '${FLAGS_base_pkginfo}'"
fi
pfile="$(basename "${ppath}")"
pmdir="${BUILD_DIR}/base-pkginfo/${pfile}"
mkdir -p "${pmdir}"
mount -rt squashfs -o loop,nodev "${ppath}" "${pmdir}"
pkginfo_lowerdirs="${pkginfo_lowerdirs}:${pmdir}"
info "Added packageinfo from '${ppath}' to base layers."
done
fi
mkdir "${BUILD_DIR}/fs-root"
mount -rt squashfs -o loop,nodev "${FLAGS_squashfs_base}" "${BUILD_DIR}/fs-root"
mkdir "${BUILD_DIR}/install-root"
mkdir "${BUILD_DIR}/workdir"
mount -t overlay overlay -o lowerdir="${BUILD_DIR}/fs-root${pkginfo_lowerdirs}",upperdir="${BUILD_DIR}/install-root",workdir="${BUILD_DIR}/workdir" "${BUILD_DIR}/install-root"
REPO_BUILD_ID=$(source "${REPO_MANIFESTS_DIR}/version.txt"; echo "$FLATCAR_BUILD_ID")
REPO_FLATCAR_VERSION=$(source "${REPO_MANIFESTS_DIR}/version.txt"; echo "$FLATCAR_VERSION")
VERSION_BOARD=$(source "${BUILD_DIR}/fs-root/usr/lib/os-release" && echo "$VERSION")
if [[ -z $REPO_BUILD_ID ]] && [[ ${COREOS_OFFICIAL:-0} -ne 1 ]]; then
BASE_SQUASHFS_BUILD_ID=$(source "${BUILD_DIR}/fs-root/usr/lib/os-release" && echo -n "$BUILD_ID")
info "This is a dev rebuild of an official release tag: No BUILD ID set in '${REPO_MANIFESTS_DIR}/version.txt'. Will use base squashfs BUILD ID for version check."
info "Repo root FLATCAR_VERSION is '$REPO_FLATCAR_VERSION', squashfs build ID is '$BASE_SQUASHFS_BUILD_ID'"
FLATCAR_VERSION="${REPO_FLATCAR_VERSION}${BASE_SQUASHFS_BUILD_ID:++}${BASE_SQUASHFS_BUILD_ID}"
info "Setting FLATCAR_VERSION to '$FLATCAR_VERSION'"
fi
if [ "$VERSION_BOARD" != "$FLATCAR_VERSION" ]; then
warn "Base squashfs version: $VERSION_BOARD"
warn "SDK board packages version: $FLATCAR_VERSION"
if [[ "${FLAGS_ignore_version_mismatch}" = "${FLAGS_TRUE}" ]] ; then
warn "Ignoring version mismatch as requested."
else
die "Version mismatch between board flatcar release and SDK container flatcar release."
fi
fi
if [[ -n "${FLAGS_metapkgs}" ]]; then
mapfile -t metapkgs < <(tr ',' '\n' <<<"${FLAGS_metapkgs}")
"emerge-${FLAGS_board}" --nodeps --buildpkgonly --usepkg n --verbose "${metapkgs[@]}"
set -- "${metapkgs[@]}" "${@}"
fi
if [[ ${#} -lt 1 ]]; then
error 'No packages or meta packages to install.'
show_help_if_requested -h
fi
info "Building '${SYSEXTNAME}' squashfs with (meta-)packages '${@}' in '${BUILD_DIR}' using '${FLAGS_compression}' compression".
for package; do
echo "Installing package into sysext image: $package"
FEATURES="-ebuild-locks" emerge \
--root="${BUILD_DIR}/install-root" \
--config-root="/build/${FLAGS_board}" \
--sysroot="/build/${FLAGS_board}" \
--usepkgonly \
--getbinpkg \
--verbose \
--jobs=${NUM_JOBS} \
"${package}"
done
# Make squashfs generation more reproducible.
export SOURCE_DATE_EPOCH=$(stat -c '%Y' "${BUILD_DIR}/fs-root/usr/lib/os-release")
# Unmount in order to get rid of the overlay
umount "${BUILD_DIR}/install-root"
umount "${BUILD_DIR}/fs-root"
if [[ "$FLAGS_generate_pkginfo" = "${FLAGS_TRUE}" ]] ; then
info " Creating pkginfo squashfs '${BUILD_DIR}/${SYSEXTNAME}_pkginfo.raw'"
mkdir -p "${BUILD_DIR}/img-pkginfo/var/db"
cp -R "${BUILD_DIR}/install-root/var/db/pkg" "${BUILD_DIR}/img-pkginfo/var/db/"
mksquashfs "${BUILD_DIR}/img-pkginfo" "${BUILD_DIR}/${SYSEXTNAME}_pkginfo.raw" \
-noappend -xattrs-exclude '^btrfs.' -comp "${FLAGS_compression}" ${FLAGS_mksquashfs_opts}
fi
info "Writing ${SYSEXTNAME}_packages.txt"
ROOT="${BUILD_DIR}/install-root" PORTAGE_CONFIGROOT="${BUILD_DIR}/install-root" \
equery --no-color list --format '$cpv::$repo' '*' > "${BUILD_DIR}/${SYSEXTNAME}_packages.txt"
if [[ "${FLAGS_strip_binaries}" = "${FLAGS_TRUE}" ]]; then
chost="$("portageq-${BOARD}" envvar CHOST)"
strip="${chost}-strip"
info "Stripping all non-stripped binaries in sysext using '${strip}'"
# Find all non-stripped binaries, remove ':' from filepath, and strip 'em
find "${BUILD_DIR}/install-root" -exec file \{\} \; \
| awk '/not stripped/ {print substr($1, 1, length($1)-1)}' \
| while read bin; do
info " ${strip} ${bin}"
"${strip}" "${bin}"
done
fi
if [[ -n "${FLAGS_manglefs_script}" ]]; then
if [[ ! -x "${FLAGS_manglefs_script}" ]]; then
die "${FLAGS_manglefs_script} is not executable"
fi
"${FLAGS_manglefs_script}" "${BUILD_DIR}/install-root"
fi
info "Removing non-/usr directories from sysext image"
for entry in "${BUILD_DIR}/install-root"/*; do
if [[ "${entry}" = */usr ]]; then
continue
fi
info " Removing ${entry##*/}"
rm -rf "${entry}"
done
mkdir -p "${BUILD_DIR}/install-root/usr/lib/extension-release.d"
version_field="${VERSION_FIELD_OVERRIDE:-VERSION_ID=${FLATCAR_VERSION_ID}}"
all_fields=(
'ID=flatcar'
"${version_field}"
"ARCHITECTURE=${ARCH}"
)
printf '%s\n' "${all_fields[@]}" >"${BUILD_DIR}/install-root/usr/lib/extension-release.d/extension-release.${SYSEXTNAME}"
info "Removing opaque directory markers to always merge all contents"
find "${BUILD_DIR}/install-root" -xdev -type d -exec sh -c 'if [ "$(attr -R -q -g overlay.opaque {} 2>/dev/null)" = y ]; then attr -R -r overlay.opaque {}; fi' \;
info "Checking for invalid file ownership"
invalid_files=$(find "${BUILD_DIR}/install-root" -user sdk -or -group sdk)
if [[ -n "${invalid_files}" ]]; then
die "Invalid file ownership: ${invalid_files}"
fi
mksquashfs "${BUILD_DIR}/install-root" "${BUILD_DIR}/${SYSEXTNAME}.raw" \
-noappend -xattrs-exclude '^btrfs.' -comp "${FLAGS_compression}" ${FLAGS_mksquashfs_opts}
rm -rf "${BUILD_DIR}"/{fs-root,install-root,workdir}
# Generate reports
mkdir "${BUILD_DIR}/img-rootfs"
mount -rt squashfs -o loop,nodev "${BUILD_DIR}/${SYSEXTNAME}.raw" "${BUILD_DIR}/img-rootfs"
write_contents "${BUILD_DIR}/img-rootfs" "${BUILD_DIR}/${SYSEXTNAME}_contents.txt"
write_contents_with_technical_details "${BUILD_DIR}/img-rootfs" "${BUILD_DIR}/${SYSEXTNAME}_contents_wtd.txt"
write_disk_space_usage_in_paths "${BUILD_DIR}/img-rootfs" "${BUILD_DIR}/${SYSEXTNAME}_disk_usage.txt"
umount "${BUILD_DIR}/img-rootfs"