xref: /freebsd/release/scripts/make-oci-image.sh (revision 3a79ca65531adf9fc8799b39407fe4d1b51906f6)
1d03c82c2SDoug Rabson#! /bin/sh
2d03c82c2SDoug Rabson
3d03c82c2SDoug Rabson# Build an Open Container Initiative (OCI) container image
4d03c82c2SDoug Rabson
5d03c82c2SDoug Rabsoncurdir=$1; shift
6d03c82c2SDoug Rabsonrev=$1; shift
7d03c82c2SDoug Rabsonbranch=$1; shift
8d03c82c2SDoug Rabsonarch=$1; shift
9d03c82c2SDoug Rabsonimage=$1; shift
10e8a5b9fdSDoug Rabsonoutput=$1; shift
11d03c82c2SDoug Rabson
12d03c82c2SDoug Rabsonmajor=${rev%.*}
13d03c82c2SDoug Rabsonminor=${rev#*.}
14d03c82c2SDoug Rabson
15d03c82c2SDoug Rabsonabi=FreeBSD:${major}:${arch}
16e8a5b9fdSDoug Rabsonver=${rev}-${branch}-${arch}
17d03c82c2SDoug Rabson
18d03c82c2SDoug Rabsonecho "Building OCI freebsd${major}-${image} image for ${abi}"
19d03c82c2SDoug Rabson
20d03c82c2SDoug Rabson. ${curdir}/tools/oci-image-${image}.conf
21d03c82c2SDoug Rabson
22e8a5b9fdSDoug Rabsoninit_repo() {
23e8a5b9fdSDoug Rabson	local workdir=$1; shift
24d03c82c2SDoug Rabson	local abi=$1; shift
25d03c82c2SDoug Rabson
26e8a5b9fdSDoug Rabson	mkdir -p ${workdir}/repos
27d03c82c2SDoug Rabson	cat > ${workdir}/repos/base.conf <<EOF
28d03c82c2SDoug RabsonFreeBSD-base: {
29d03c82c2SDoug Rabson  url: "file:///usr/obj/usr/src/repo/${abi}/latest"
30d03c82c2SDoug Rabson  signature_type: "none"
31d03c82c2SDoug Rabson  fingerprints: "none"
32d03c82c2SDoug Rabson}
33d03c82c2SDoug RabsonEOF
34d03c82c2SDoug Rabson	cp /etc/pkg/FreeBSD.conf ${workdir}/repos
35d03c82c2SDoug Rabson}
36d03c82c2SDoug Rabson
37e8a5b9fdSDoug Rabson# Install packages using pkg(8) into a container with rootfs at $3
38d03c82c2SDoug Rabsoninstall_packages() {
39d03c82c2SDoug Rabson	local abi=$1; shift
40d03c82c2SDoug Rabson	local workdir=$1; shift
41e8a5b9fdSDoug Rabson	local rootdir=${workdir}/rootfs
42d03c82c2SDoug Rabson	if [ ! -d ${rootdir}/usr/share/keys/pkg/trusted ]; then
43d03c82c2SDoug Rabson		mkdir -p ${rootdir}/usr/share/keys/pkg/trusted
44d03c82c2SDoug Rabson	fi
45d03c82c2SDoug Rabson	cp /usr/share/keys/pkg/trusted/* ${rootdir}/usr/share/keys/pkg/trusted
46d03c82c2SDoug Rabson	# We install the packages and then remove repository metadata (keeping the
47d03c82c2SDoug Rabson	# metadata for what was installed). This trims more than 40Mb from the
48d03c82c2SDoug Rabson	# resulting image.
49d03c82c2SDoug Rabson	env IGNORE_OSVERSION=yes ABI=${abi} pkg --rootdir ${rootdir} --repo-conf-dir ${workdir}/repos \
50d03c82c2SDoug Rabson		install -yq "$@" || exit $?
51d03c82c2SDoug Rabson	rm -rf ${rootdir}/var/db/pkg/repos
52d03c82c2SDoug Rabson}
53d03c82c2SDoug Rabson
54e8a5b9fdSDoug Rabsonset_cmd() {
55e8a5b9fdSDoug Rabson	local workdir=$1; shift
56e8a5b9fdSDoug Rabson	oci_cmd="$@"
57e8a5b9fdSDoug Rabson}
58e8a5b9fdSDoug Rabson
59e8a5b9fdSDoug Rabson# Convert FreeBSD architecture to OCI-style. See
60e8a5b9fdSDoug Rabson# https://github.com/containerd/platforms/blob/main/platforms.go for details
61e8a5b9fdSDoug Rabsonnormalize_arch() {
62e8a5b9fdSDoug Rabson	local arch=$1; shift
63e8a5b9fdSDoug Rabson	case ${arch} in
64e8a5b9fdSDoug Rabson		i386)
65e8a5b9fdSDoug Rabson		       arch=386
66e8a5b9fdSDoug Rabson		       ;;
67e8a5b9fdSDoug Rabson		aarch64)
68e8a5b9fdSDoug Rabson		       arch=arm64
69e8a5b9fdSDoug Rabson		       ;;
70e8a5b9fdSDoug Rabson		amd64) ;;
71e8a5b9fdSDoug Rabson		riscv64) ;;
72e8a5b9fdSDoug Rabson		*)
73e8a5b9fdSDoug Rabson			echo "Architecture ${arch} not supported for container images"
74e8a5b9fdSDoug Rabson			;;
75e8a5b9fdSDoug Rabson	esac
76e8a5b9fdSDoug Rabson	echo ${arch}
77e8a5b9fdSDoug Rabson}
78e8a5b9fdSDoug Rabson
79e8a5b9fdSDoug Rabsoncreate_container() {
80e8a5b9fdSDoug Rabson	local workdir=$1; shift
81e8a5b9fdSDoug Rabson	local base_workdir=$1; shift
82e8a5b9fdSDoug Rabson	oci_cmd=
83e8a5b9fdSDoug Rabson	if [ -d ${workdir}/rootfs ]; then
84e8a5b9fdSDoug Rabson		chflags -R 0 ${workdir}/rootfs
85e8a5b9fdSDoug Rabson		rm -rf ${workdir}/rootfs
86e8a5b9fdSDoug Rabson	fi
87e8a5b9fdSDoug Rabson	mkdir -p ${workdir}/rootfs
88e8a5b9fdSDoug Rabson	if [ "${base_workdir}" != "" ]; then
89e8a5b9fdSDoug Rabson		tar -C ${workdir}/rootfs -xf ${base_workdir}/rootfs.tar.gz
90e8a5b9fdSDoug Rabson	fi
91e8a5b9fdSDoug Rabson}
92e8a5b9fdSDoug Rabson
93e8a5b9fdSDoug Rabsoncommit_container() {
94e8a5b9fdSDoug Rabson	local workdir=$1; shift
95e8a5b9fdSDoug Rabson	local image=$1; shift
96e8a5b9fdSDoug Rabson	local output=$1; shift
97e8a5b9fdSDoug Rabson
98*3a79ca65SDoug Rabson	# Note: the diff_id (needed for image config) is the hash of the
99*3a79ca65SDoug Rabson	# uncompressed tar.
100*3a79ca65SDoug Rabson	#
101*3a79ca65SDoug Rabson	# For compatibility with Podman, we must disable sparse-file
102*3a79ca65SDoug Rabson	# handling. See https://github.com/containers/podman/issues/25270 for
103*3a79ca65SDoug Rabson	# more details.
104*3a79ca65SDoug Rabson	tar -C ${workdir}/rootfs --strip-components 1 --no-read-sparse -cf ${workdir}/rootfs.tar .
105e8a5b9fdSDoug Rabson	local diff_id=$(sha256 -q < ${workdir}/rootfs.tar)
106e8a5b9fdSDoug Rabson	gzip -f ${workdir}/rootfs.tar
107e8a5b9fdSDoug Rabson	local create_time=$(date -u +%Y-%m-%dT%TZ)
108e8a5b9fdSDoug Rabson	local root_hash=$(sha256 -q < ${workdir}/rootfs.tar.gz)
109e8a5b9fdSDoug Rabson	local root_size=$(stat -f %z ${workdir}/rootfs.tar.gz)
110e8a5b9fdSDoug Rabson
111e8a5b9fdSDoug Rabson	oci_arch=$(normalize_arch ${arch})
112e8a5b9fdSDoug Rabson
113e8a5b9fdSDoug Rabson	config=
114e8a5b9fdSDoug Rabson	if [ -n "${oci_cmd}" ]; then
115e8a5b9fdSDoug Rabson		config=",\"config\":{\"cmd\":[\"${oci_cmd}\"]}"
116e8a5b9fdSDoug Rabson	fi
117e8a5b9fdSDoug Rabson	echo "{\"created\":\"${create_time}\",\"architecture\":\"${oci_arch}\",\"os\":\"freebsd\"${config},\"rootfs\":{\"type\":\"layers\",\"diff_ids\":[\"sha256:${diff_id}\"]},\"history\":[{\"created\":\"${create_time}\",\"created_by\":\"make-oci-image.sh\"}]}" > ${workdir}/config.json
118e8a5b9fdSDoug Rabson	local config_hash=$(sha256 -q < ${workdir}/config.json)
119e8a5b9fdSDoug Rabson	local config_size=$(stat -f %z ${workdir}/config.json)
120e8a5b9fdSDoug Rabson
121e8a5b9fdSDoug Rabson	echo "{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"config\":{\"mediaType\":\"application/vnd.oci.image.config.v1+json\",\"digest\":\"sha256:${config_hash}\",\"size\":${config_size}},\"layers\":[{\"mediaType\":\"application/vnd.oci.image.layer.v1.tar+gzip\",\"digest\":\"sha256:${root_hash}\",\"size\":${root_size}}],\"annotations\":{}}" > ${workdir}/manifest.json
122e8a5b9fdSDoug Rabson	local manifest_hash=$(sha256 -q < ${workdir}/manifest.json)
123e8a5b9fdSDoug Rabson	local manifest_size=$(stat -f %z ${workdir}/manifest.json)
124e8a5b9fdSDoug Rabson
125e8a5b9fdSDoug Rabson	mkdir -p ${workdir}/oci/blobs/sha256
126e8a5b9fdSDoug Rabson	echo "{\"imageLayoutVersion\": \"1.0.0\"}" > ${workdir}/oci/oci-layout
127e8a5b9fdSDoug Rabson	echo "{\"schemaVersion\":2,\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:${manifest_hash}\",\"size\":${manifest_size},\"annotations\":{\"org.opencontainers.image.ref.name\":\"freebsd-${image}:${ver}\"}}]}" > ${workdir}/oci/index.json
128e8a5b9fdSDoug Rabson	ln ${workdir}/rootfs.tar.gz ${workdir}/oci/blobs/sha256/${root_hash}
129e8a5b9fdSDoug Rabson	ln ${workdir}/config.json ${workdir}/oci/blobs/sha256/${config_hash}
130e8a5b9fdSDoug Rabson	ln ${workdir}/manifest.json ${workdir}/oci/blobs/sha256/${manifest_hash}
131e8a5b9fdSDoug Rabson
132e8a5b9fdSDoug Rabson	tar -C ${workdir}/oci --xz --strip-components 1 --no-read-sparse -a -cf ${output} .
133e8a5b9fdSDoug Rabson}
134e8a5b9fdSDoug Rabson
135e8a5b9fdSDoug Rabson# Prefix with "container-image-" so that we can create a unique work area under
136e8a5b9fdSDoug Rabson# ${.OBJDIR}. We can assume that make has set our working directory to
137e8a5b9fdSDoug Rabson# ${.OBJDIR}.
138e8a5b9fdSDoug Rabsonworkdir=${PWD}/container-image-${image}
139e8a5b9fdSDoug Rabsoninit_repo ${workdir} ${abi}
140e8a5b9fdSDoug Rabson
141d03c82c2SDoug Rabsonif [ -n "${OCI_BASE_IMAGE}" ]; then
142e8a5b9fdSDoug Rabson	base_workdir=${PWD}/container-image-${OCI_BASE_IMAGE}
143d03c82c2SDoug Rabsonelse
144e8a5b9fdSDoug Rabson	base_workdir=
145d03c82c2SDoug Rabsonfi
146d03c82c2SDoug Rabson
147e8a5b9fdSDoug Rabsoncreate_container ${workdir} ${base_workdir}
148d03c82c2SDoug Rabsonoci_image_build
149e8a5b9fdSDoug Rabsoncommit_container ${workdir} ${image} ${output}
150