forked from coreos/coreos-assembler
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcmd-buildextend-metal
executable file
·358 lines (319 loc) · 12.4 KB
/
cmd-buildextend-metal
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
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#!/usr/bin/env bash
set -euo pipefail
dn=$(dirname "$0")
# shellcheck source=src/cmdlib.sh
. "${dn}"/cmdlib.sh
# IBM SecureExecution
secure_execution=
image_suffix=
# This script is used for creating both the bare metal and the canonical VM
# image (qemu). `buildextend-qemu` is a symlink to `buildextend-metal`.
case "$(basename "$0")" in
"cmd-buildextend-metal") image_type=metal;;
"cmd-buildextend-metal4k") image_type=metal4k;;
"cmd-buildextend-dasd") image_type=dasd;;
"cmd-buildextend-qemu") image_type=qemu;;
"cmd-buildextend-secex")
secure_execution=1
image_type=qemu
image_suffix=-secex
;;
*) fatal "called as unexpected name $0";;
esac
print_help() {
cat 1>&2 <<EOF
Usage: coreos-assembler buildextend-${image_type} --help
coreos-assembler buildextend-${image_type} [--build ID]
Build a bare metal image.
EOF
}
# Parse options
hostkey=
genprotimgvm=/data.secex/genprotimgvm.qcow2
ignition_pubkey=
rc=0
build=
force=
options=$(getopt --options h --longoptions help,force,build:,hostkey:,genprotimgvm: -- "$@") || rc=$?
[ $rc -eq 0 ] || {
print_help
exit 1
}
eval set -- "$options"
while true; do
case "$1" in
-h | --help)
print_help
exit 0
;;
--force)
force=1
;;
--build)
build=$2
shift
;;
--hostkey)
hostkey=$(realpath "$2")
shift
;;
--genprotimgvm)
genprotimgvm="$2"
shift
;;
--)
shift
break
;;
-*)
fatal "$0: unrecognized option: $1"
;;
*)
break
;;
esac
shift
done
if [ $# -ne 0 ]; then
print_help
fatal "Too many arguments passed"
fi
case "$basearch" in
"x86_64"|"aarch64"|"s390x"|"ppc64le") ;;
*) fatal "$basearch is not supported for this command" ;;
esac
if [[ "$basearch" != "s390x" && $image_type == dasd ]]; then
fatal "$basearch is not supported for building dasd images"
fi
# shellcheck disable=SC2031
export LIBGUESTFS_BACKEND=direct
export IMAGE_TYPE="${image_type}"
prepare_build
if [ -z "${build}" ]; then
build=$(get_latest_build)
if [ -z "${build}" ]; then
fatal "No build found."
fi
fi
builddir=$(get_build_dir "$build")
if [ ! -d "${builddir}" ]; then
fatal "Build dir ${builddir} does not exist."
fi
# add building sempahore
build_semaphore="${builddir}/.${image_type}.building"
if [ -e "${build_semaphore}" ]; then
fatal "${build_semaphore} found: another process is building ${image_type}"
fi
touch "${build_semaphore}"
trap 'rm -f ${build_semaphore}' EXIT
# check if the image already exists in the meta.json
if [ -z "${force}" ]; then
meta_img=$(meta_key "images.${image_type}${image_suffix}.path")
if [ "${meta_img}" != "None" ]; then
echo "${image_type}${image_suffix} image already exists:"
echo "$meta_img"
exit 0
fi
fi
# reread these values from the build itself rather than rely on the ones loaded
# by prepare_build since the config might've changed since then
name=$(meta_key name)
ref=$(meta_key ref)
if [ "${ref}" = "None" ]; then
ref=""
fi
commit=$(meta_key ostree-commit)
ostree_repo=${tmprepo}
# Ensure that we have the cached unpacked commit
import_ostree_commit_for_build "${build}"
# Note this overwrote the bits generated in prepare_build
# for image_json. In the future we expect to split prepare_build
# into prepare_ostree_build and prepare_diskimage_build; the
# latter path would only run this.
image_json=${workdir}/tmp/image.json
image_format=raw
if [[ $image_type == qemu ]]; then
image_format=qcow2
fi
img=${name}-${build}-${image_type}${image_suffix}.${basearch}.${image_format}
path=${PWD}/${img}
ignition_platform_id="${image_type}"
# dasd and metal4k are different disk formats, but they're still metal
if [ "${image_type}" = dasd ] || [ "${image_type}" = metal4k ]; then
ignition_platform_id=metal
fi
# We do some extra handling of the rootfs here; it feeds into size estimation.
rootfs_type=$(jq -re .rootfs < "${image_json}")
deploy_via_container=""
if jq -re '.["deploy-via-container"]' < "${image_json}"; then
deploy_via_container="true"
fi
# OStree container ociarchive file path
ostree_container="${builddir}/$(meta_key images.ostree.path)"
container_imgref=$(jq -r '.["container-imgref"]//""' < "${image_json}")
if [ -z "${container_imgref}" ]; then
# If no container_imgref was set let's just set it to some professional
# looking default. The name of the ociarchive file should suffice.
container_imgref="ostree-image-signed:oci-archive:/$(basename "${ostree_container}")"
fi
# fs-verity requires block size = page size. We need to take that into account
# in the disk size estimation due to higher fragmentation on larger blocks.
BLKSIZE=""
if [ "${rootfs_type}" = "ext4verity" ]; then
BLKSIZE="$(getconf PAGE_SIZE)"
fi
disk_args=()
qemu_args=()
# SecureExecution extra stuff
if [[ $secure_execution -eq "1" ]]; then
ignition_pubkey=$(mktemp -p "${tmp_builddir}")
disk_args+=("--with-secure-execution" "--write-ignition-pubkey-to" "${ignition_pubkey}")
if [ -z "${hostkey}" ]; then
if [ ! -f "${genprotimgvm}" ]; then
fatal "No genprotimgvm provided at ${genprotimgvm}"
fi
genprotimg_img="${PWD}/secex-genprotimg.img"
qemu-img create -f raw "${genprotimg_img}" 512M
mkfs.ext4 "${genprotimg_img}"
qemu_args+=("-drive" "if=none,id=genprotimg,format=raw,file=${genprotimg_img}" \
"-device" "virtio-blk,serial=genprotimg,drive=genprotimg")
else
qemu_args+=("-drive" "if=none,id=hostkey,format=raw,file=$hostkey,readonly=on" \
"-device" "virtio-blk,serial=hostkey,drive=hostkey")
fi
fi
echo "Estimating disk size..."
# The additional 35% here is obviously a hack, but we can't easily completely fill the filesystem,
# and doing so has apparently negative performance implications.
/usr/lib/coreos-assembler/estimate-commit-disk-size ${BLKSIZE:+--blksize ${BLKSIZE}} --repo "$ostree_repo" "$commit" --add-percent 35 > "$PWD/tmp/ostree-size.json"
rootfs_size_mb="$(jq '."estimate-mb".final' "$PWD/tmp/ostree-size.json")"
# The minimum size of a disk image we'll need will be the rootfs_size
# estimate plus the size of the non-root partitions. We'll use this
# size for the metal/dasd images, but for the IaaS/virt image we'll use
# the size set in the configs since some of them have minimum sizes that
# the platforms require and we want a "default" disk size that has some
# free space.
nonroot_partition_sizes=513
# In the Secure Execution case, we need to also include the sizes of the verity
# partitions so that they don't "eat into" the 35% buffer (though note this is
# all blown away on first boot anyway).
if [[ $secure_execution -eq "1" ]]; then
nonroot_partition_sizes=$((nonroot_partition_sizes + 128 + 256 + 1))
fi
metal_image_size_mb="$(( rootfs_size_mb + nonroot_partition_sizes ))"
cloud_image_size_mb="$(jq -r ".size*1024" < "${image_json}")"
echo "Disk sizes: metal: ${metal_image_size_mb}M (estimated), cloud: ${cloud_image_size_mb}M"
if [ "${image_type}" == metal4k ]; then
disk_args+=("--no-x86-bios-bootloader")
fi
set -x
kargs="$(python3 -c 'import sys, json; args = json.load(sys.stdin)["extra-kargs"]; print(" ".join(args))' < "${image_json}")"
kargs="$kargs ignition.platform.id=$ignition_platform_id"
qemu-img create -f ${image_format} "${path}.tmp" "${metal_image_size_mb}M"
extra_target_device_opts=""
# we need 4096 block size for ECKD DASD and (obviously) metal4k
if [[ $image_type == dasd || $image_type == metal4k ]]; then
extra_target_device_opts=",physical_block_size=4096,logical_block_size=4096"
fi
qemu_args+=("-drive" "if=none,id=target,format=${image_format},file=${path}.tmp,cache=unsafe" \
"-device" "virtio-blk,serial=target,drive=target${extra_target_device_opts}")
# Generate the JSON describing the disk we want to build
image_dynamic_yaml="${tmp_builddir}/image-dynamic.yaml"
image_dynamic_json="${tmp_builddir}/image-dynamic.json"
image_for_disk_json="${tmp_builddir}/image-for-disk.json"
cat >"${image_dynamic_yaml}" << EOF
# Used by create_disk.sh
buildid: "${build}"
imgid: "${img}"
ostree-commit: "${commit}"
# Used by both create_disk.sh and runvm-osbuild
container-imgref: "${container_imgref}"
deploy-via-container: "${deploy_via_container}"
osname: "${name}"
ostree-container: "${ostree_container}"
ostree-ref: "${ref}"
# Used by runvm-osbuild
image-type: "${image_type}"
ostree-repo: "${ostree_repo}"
metal-image-size: "${metal_image_size_mb}"
cloud-image-size: "${cloud_image_size_mb}"
EOF
yaml2json "${image_dynamic_yaml}" "${image_dynamic_json}"
cat "${image_json}" "${image_dynamic_json}" | jq -s add > "${image_for_disk_json}"
platforms_json="${tmp_builddir}/platforms.json"
yaml2json "${configdir}/platforms.yaml" "${platforms_json}"
# Currently we only support OSBuild for qemu and metal disk images
if [ "${image_type}" == "qemu" ] || [ "${image_type}" == "metal" ] || [ "${image_type}" == "metal4k" ]; then
# Right now we don't have support fully fleshed out for secex on s390x
if [[ $secure_execution -ne "1" ]]; then
OSBUILD_SUPPORTED=1
fi
fi
# Run with OSBuild if it's supported and requested, otherwise use create_disk
if [ "${OSBUILD_SUPPORTED:-}" != "" ] && [ "${COSA_USE_OSBUILD:-}" != "" ]; then
# In the jenkins pipelines we build the qemu image first and that operation
# will do a lot of the same work required for later artifacts (metal, metal4k, etc)
# so we want the cached output from that run to persist. The later artifacts get
# built in parallel, so we need to be able to access the cache by multiple processes,
# so for those we'll set `snapshot=on` so that each will get their own disk image.
# This is OK because we don't checkpoint (cache) any of those stages.
[ "${image_type}" == "qemu" ] && snapshot="off" || snapshot="on"
runvm_with_cache_snapshot "$snapshot" -- /usr/lib/coreos-assembler/runvm-osbuild \
--config "${image_for_disk_json}" \
--mpp "/usr/lib/coreos-assembler/osbuild-manifests/coreos.osbuild.${basearch}.mpp.yaml" \
--filepath "${path}.tmp"
else
runvm "${qemu_args[@]}" -- \
/usr/lib/coreos-assembler/create_disk.sh \
--config "${image_for_disk_json}" \
--kargs "${kargs}" \
--platform "${ignition_platform_id}" \
--platforms-json "${platforms_json}" \
"${disk_args[@]}"
# Now that we've created the image let's resize it to the desired
# size for qemu (and all images based on it).
if [[ $image_type == qemu ]]; then
qemu-img resize "${path}.tmp" "${cloud_image_size_mb}M"
fi
fi
if [[ $secure_execution -eq "1" && -z "${hostkey}" ]]; then
/usr/lib/coreos-assembler/secex-genprotimgvm-scripts/runvm.sh \
--genprotimgvm "${genprotimgvm}" -- "${qemu_args[@]}"
fi
/usr/lib/coreos-assembler/finalize-artifact "${path}.tmp" "${path}"
sha256=$(sha256sum_str < "${img}")
cosa meta --workdir "${workdir}" --build "${build}" --dump | python3 -c "
import sys, json
j = json.load(sys.stdin)
j['images']['${image_type}${image_suffix}'] = {
'path': '${img}',
'sha256': '${sha256}',
'size': $(stat -c '%s' "${img}")
}
json.dump(j, sys.stdout, indent=4)
" | jq -s add > "meta.json.new"
# one more artifact for Secure Execution
if [[ -n "${ignition_pubkey}" ]]; then
gpg_key=${name}-${build}-ignition-secex-key.gpg.pub
python3 -c "
import sys, json
j = json.load(sys.stdin)
j['images']['ignition-gpg-key'] = {
'path': '${gpg_key}',
'sha256': '$(sha256sum_str < "${ignition_pubkey}")',
'size': $(stat -c '%s' "${ignition_pubkey}"),
'skip-compression': True
}
json.dump(j, sys.stdout, indent=4)
" < "meta.json.new" | jq -s add > "key.json"
mv key.json meta.json.new
/usr/lib/coreos-assembler/finalize-artifact "${ignition_pubkey}" "${builddir}/${gpg_key}"
fi
# and now the crucial bits
cosa meta --workdir "${workdir}" --build "${build}" --artifact "${image_type}" --artifact-json "$(readlink -f meta.json.new)"
/usr/lib/coreos-assembler/finalize-artifact "${img}" "${builddir}/${img}"
# Quiet for the rest of this so the last thing we see is a success message
set +x
# clean up the tmpbuild
rm -rf "${tmp_builddir}"
echo "Successfully generated: ${img}"