summaryrefslogtreecommitdiff
path: root/release/scripts/make-oci-image.sh
blob: f8ea679bfd187b6345716a6495be69836e34a1b3 (plain)
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
#! /bin/sh

# Build an Open Container Initiative (OCI) container image

curdir=$1; shift
rev=$1; shift
branch=$1; shift
arch=$1; shift
image=$1; shift
output=$1; shift

major=${rev%.*}
minor=${rev#*.}

abi=FreeBSD:${major}:${arch}
ver=${rev}-${branch}-${arch}

echo "Building OCI freebsd${major}-${image} image for ${abi}"

. ${curdir}/tools/oci-image-${image}.conf

init_repo() {
	local workdir=$1; shift
	local abi=$1; shift
	local srcdir=$(realpath ${curdir}/..)

	mkdir -p ${workdir}/repos
	cat > ${workdir}/repos/base.conf <<EOF
FreeBSD-base: {
  url: "file:///usr/obj${srcdir}/repo/${abi}/latest"
  signature_type: "none"
  fingerprints: "none"
}
EOF
}

# Install packages using pkg(8) into a container with rootfs at $3
install_packages() {
	local abi=$1; shift
	local workdir=$1; shift
	local rootdir=${workdir}/rootfs

	# Make sure we have the keys needed for verifying package integrity if
	# not already added by a parent image.
	if [ ! -d ${rootdir}/usr/share/keys/pkg/trusted ]; then
		mkdir -p ${rootdir}/usr/share/keys/pkg/trusted
	fi
	for i in ${curdir}/../share/keys/pkg/trusted/pkg.*; do
		if [ ! -f ${rootdir}/usr/share/keys/pkg/trusted/$(basename $i) ]; then
			cp $i ${rootdir}/usr/share/keys/pkg/trusted
		fi
	done

	# We install the packages and then remove repository metadata (keeping the
	# metadata for what was installed). This trims more than 40Mb from the
	# resulting image.
	env IGNORE_OSVERSION=yes ABI=${abi} pkg --rootdir ${rootdir} --repo-conf-dir ${workdir}/repos \
		install -yq -g "$@" || exit $?
	rm -rf ${rootdir}/var/db/pkg/repos
}

set_cmd() {
	local workdir=$1; shift
	oci_cmd="$@"
}

# Convert FreeBSD architecture to OCI-style. See
# https://github.com/containerd/platforms/blob/main/platforms.go for details
normalize_arch() {
	local arch=$1; shift
	case ${arch} in
		i386)
		       arch=386
		       ;;
		aarch64)
		       arch=arm64
		       ;;
		amd64) ;;
		riscv64) ;;
		*)
			echo "Architecture ${arch} not supported for container images"
			;;
	esac
	echo ${arch}
}

create_container() {
	local workdir=$1; shift
	local base_workdir=$1; shift
	oci_cmd=
	if [ -d ${workdir}/rootfs ]; then
		chflags -R 0 ${workdir}/rootfs
		rm -rf ${workdir}/rootfs
	fi
	mkdir -p ${workdir}/rootfs
	if [ "${base_workdir}" != "" ]; then
		tar -C ${workdir}/rootfs -xf ${base_workdir}/rootfs.tar.gz
	fi
}

commit_container() {
	local workdir=$1; shift
	local image=$1; shift
	local output=$1; shift

	# Note: the diff_id (needed for image config) is the hash of the
	# uncompressed tar.
	#
	# For compatibility with Podman, we must disable sparse-file
	# handling. See https://github.com/containers/podman/issues/25270 for
	# more details.
	tar -C ${workdir}/rootfs --strip-components 1 --no-read-sparse -cf ${workdir}/rootfs.tar .
	local diff_id=$(sha256 -q < ${workdir}/rootfs.tar)
	gzip -f ${workdir}/rootfs.tar
	local create_time=$(date -u +%Y-%m-%dT%TZ)
	local root_hash=$(sha256 -q < ${workdir}/rootfs.tar.gz)
	local root_size=$(stat -f %z ${workdir}/rootfs.tar.gz)

	oci_arch=$(normalize_arch ${arch})

	config=
	if [ -n "${oci_cmd}" ]; then
		config=",\"config\":{\"cmd\":[\"${oci_cmd}\"]}"
	fi
	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
	local config_hash=$(sha256 -q < ${workdir}/config.json)
	local config_size=$(stat -f %z ${workdir}/config.json)

	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
	local manifest_hash=$(sha256 -q < ${workdir}/manifest.json)
	local manifest_size=$(stat -f %z ${workdir}/manifest.json)

	mkdir -p ${workdir}/oci/blobs/sha256
	echo "{\"imageLayoutVersion\": \"1.0.0\"}" > ${workdir}/oci/oci-layout
	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
	ln ${workdir}/rootfs.tar.gz ${workdir}/oci/blobs/sha256/${root_hash}
	ln ${workdir}/config.json ${workdir}/oci/blobs/sha256/${config_hash}
	ln ${workdir}/manifest.json ${workdir}/oci/blobs/sha256/${manifest_hash}

	tar -C ${workdir}/oci --xz --strip-components 1 --no-read-sparse -a -cf ${output} .
}

# Prefix with "container-image-" so that we can create a unique work area under
# ${.OBJDIR}. We can assume that make has set our working directory to
# ${.OBJDIR}.
workdir=${PWD}/container-image-${image}
init_repo ${workdir} ${abi}

if [ -n "${OCI_BASE_IMAGE}" ]; then
	base_workdir=${PWD}/container-image-${OCI_BASE_IMAGE}
else
	base_workdir=
fi

create_container ${workdir} ${base_workdir}
oci_image_build
commit_container ${workdir} ${image} ${output}