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