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 local srcdir=$(realpath ${curdir}/..) 26 27 mkdir -p ${workdir}/repos 28 cat > ${workdir}/repos/base.conf <<EOF 29FreeBSD-base: { 30 url: "file:///usr/obj${srcdir}/repo/${abi}/latest" 31 signature_type: "none" 32 fingerprints: "none" 33} 34EOF 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 -g "$@" || 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 99 # uncompressed tar. 100 # 101 # For compatibility with Podman, we must disable sparse-file 102 # handling. See https://github.com/containers/podman/issues/25270 for 103 # more details. 104 tar -C ${workdir}/rootfs --strip-components 1 --no-read-sparse -cf ${workdir}/rootfs.tar . 105 local diff_id=$(sha256 -q < ${workdir}/rootfs.tar) 106 gzip -f ${workdir}/rootfs.tar 107 local create_time=$(date -u +%Y-%m-%dT%TZ) 108 local root_hash=$(sha256 -q < ${workdir}/rootfs.tar.gz) 109 local root_size=$(stat -f %z ${workdir}/rootfs.tar.gz) 110 111 oci_arch=$(normalize_arch ${arch}) 112 113 config= 114 if [ -n "${oci_cmd}" ]; then 115 config=",\"config\":{\"cmd\":[\"${oci_cmd}\"]}" 116 fi 117 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 118 local config_hash=$(sha256 -q < ${workdir}/config.json) 119 local config_size=$(stat -f %z ${workdir}/config.json) 120 121 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 122 local manifest_hash=$(sha256 -q < ${workdir}/manifest.json) 123 local manifest_size=$(stat -f %z ${workdir}/manifest.json) 124 125 mkdir -p ${workdir}/oci/blobs/sha256 126 echo "{\"imageLayoutVersion\": \"1.0.0\"}" > ${workdir}/oci/oci-layout 127 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 128 ln ${workdir}/rootfs.tar.gz ${workdir}/oci/blobs/sha256/${root_hash} 129 ln ${workdir}/config.json ${workdir}/oci/blobs/sha256/${config_hash} 130 ln ${workdir}/manifest.json ${workdir}/oci/blobs/sha256/${manifest_hash} 131 132 tar -C ${workdir}/oci --xz --strip-components 1 --no-read-sparse -a -cf ${output} . 133} 134 135# Prefix with "container-image-" so that we can create a unique work area under 136# ${.OBJDIR}. We can assume that make has set our working directory to 137# ${.OBJDIR}. 138workdir=${PWD}/container-image-${image} 139init_repo ${workdir} ${abi} 140 141if [ -n "${OCI_BASE_IMAGE}" ]; then 142 base_workdir=${PWD}/container-image-${OCI_BASE_IMAGE} 143else 144 base_workdir= 145fi 146 147create_container ${workdir} ${base_workdir} 148oci_image_build 149commit_container ${workdir} ${image} ${output} 150