From 71dc8125948405acda528804fbd295b338862b62 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 5 Mar 2024 21:22:34 -0500 Subject: [PATCH] Add `cosa buildextend-layered` As part of https://github.com/openshift/os/issues/799, we'll want to build the "OCP node" image as a layered image on top of the RHCOS base image. Eventually, this image should be built outside our pipelines and more like the rest of OpenShift container images. But for now, let's build it ourselves. This allows us to prove out the idea without yet requiring changes in the rest of OpenShift. The script added here looks wordy, but it's really trivial. It's basically a glorified wrapper around `podman build` and `skopeo copy` so that the built OCI image ends up in our `meta.json`. --- cmd/coreos-assembler.go | 2 +- pkg/builds/cosa_v1.go | 3 +- pkg/builds/schema_doc.go | 15 +++- src/cmd-buildextend-layered | 155 ++++++++++++++++++++++++++++++++++++ src/v1.json | 13 +++ 5 files changed, 185 insertions(+), 3 deletions(-) create mode 100755 src/cmd-buildextend-layered diff --git a/cmd/coreos-assembler.go b/cmd/coreos-assembler.go index 2824864fa5..0fd213ddc0 100644 --- a/cmd/coreos-assembler.go +++ b/cmd/coreos-assembler.go @@ -14,7 +14,7 @@ import ( // commands we'd expect to use in the local dev path var buildCommands = []string{"init", "fetch", "build", "run", "prune", "clean", "list"} var advancedBuildCommands = []string{"buildfetch", "buildupload", "oc-adm-release", "push-container"} -var buildextendCommands = []string{"aliyun", "applehv", "aws", "azure", "digitalocean", "exoscale", "extensions-container", "gcp", "hashlist-experimental", "hyperv", "ibmcloud", "kubevirt", "live", "metal", "metal4k", "nutanix", "openstack", "qemu", "secex", "virtualbox", "vmware", "vultr"} +var buildextendCommands = []string{"aliyun", "applehv", "aws", "azure", "digitalocean", "exoscale", "extensions-container", "gcp", "hashlist-experimental", "hyperv", "ibmcloud", "kubevirt", "layered", "live", "metal", "metal4k", "nutanix", "openstack", "qemu", "secex", "virtualbox", "vmware", "vultr"} var utilityCommands = []string{"aws-replicate", "compress", "copy-container", "koji-upload", "kola", "push-container-manifest", "remote-build-container", "remote-prune", "remote-session", "sign", "tag", "update-variant"} var otherCommands = []string{"shell", "meta"} diff --git a/pkg/builds/cosa_v1.go b/pkg/builds/cosa_v1.go index 443bfa9eb4..d6bbb6fdaa 100644 --- a/pkg/builds/cosa_v1.go +++ b/pkg/builds/cosa_v1.go @@ -1,7 +1,7 @@ package builds // generated by 'make schema' -// source hash: 4c19aed3b3d84af278780bff63728510bb3e70613e4c4eef8cabd7939eb31bd8 +// source hash: 6fb3ed460736f7d07147e5aecf581b1a417206ed30423d642620a2a0577b7952 type AdvisoryDiff []AdvisoryDiffItems @@ -60,6 +60,7 @@ type Build struct { InputHashOfTheRpmOstree string `json:"rpm-ostree-inputhash"` Koji *Koji `json:"koji,omitempty"` KubevirtContainer *Image `json:"kubevirt,omitempty"` + LayeredImages map[string]Artifact `json:"layered-images,omitempty"` MetaStamp float64 `json:"coreos-assembler.meta-stamp,omitempty"` Name string `json:"name"` Oscontainer *Image `json:"oscontainer,omitempty"` diff --git a/pkg/builds/schema_doc.go b/pkg/builds/schema_doc.go index 37e2269110..45f0cdb77b 100644 --- a/pkg/builds/schema_doc.go +++ b/pkg/builds/schema_doc.go @@ -1,5 +1,5 @@ // Generated by ./generate-schema.sh -// Source hash: 4c19aed3b3d84af278780bff63728510bb3e70613e4c4eef8cabd7939eb31bd8 +// Source hash: 6fb3ed460736f7d07147e5aecf581b1a417206ed30423d642620a2a0577b7952 // DO NOT EDIT package builds @@ -230,6 +230,7 @@ var generatedSchemaJSON = `{ "ibmcloud", "powervs", "images", + "layered-images", "koji", "oscontainer", "extensions", @@ -741,6 +742,18 @@ var generatedSchemaJSON = `{ } } }, + "layered-images": { + "$id": "#/properties/layered-images", + "type": "object", + "title": "Layered Images", + "propertyNames": { + "pattern": "^[a-z][-a-z0-9]*$" + }, + "additionalProperties": { + "type": "object", + "$ref": "#/definitions/artifact" + } + }, "ostree-commit": { "$id": "#/properties/ostree-commit", "type": "string", diff --git a/src/cmd-buildextend-layered b/src/cmd-buildextend-layered new file mode 100755 index 0000000000..1cf6238f14 --- /dev/null +++ b/src/cmd-buildextend-layered @@ -0,0 +1,155 @@ +#!/usr/bin/env bash +set -euo pipefail + +dn=$(dirname "$0") +# shellcheck source=src/cmdlib.sh +. "${dn}"/cmdlib.sh + +print_help() { + cat 1>&2 < + + Build a layered image on top of base oscontainer. +EOF +} + +if [ ! -f /etc/cosa-supermin ] && [ -z "${COSA_BUILDEXTEND_LAYERED_FORCE_INNER:-}" ]; then + + # This runs outside of supermin + + # Parse options + build= + force= + rc=0 + options=$(getopt --options h --longoptions help,force,build: -- "$@") || 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 + ;; + --) + shift + break + ;; + -*) + fatal "$0: unrecognized option: $1" + ;; + *) + break + ;; + esac + shift + done + + if [ $# = 0 ]; then + print_help + exit 1 + fi + + name=$1; shift + + containerfile="src/config/Containerfile.${name}" + if [ ! -f "${containerfile}" ]; then + fatal "Containerfile does not exist: ${containerfile}" + fi + + if [ -z "${build}" ]; then + build=$(get_latest_build) + if [ -z "${build}" ]; then + fatal "No build found." + fi + fi + + osname=$(cosa meta --build="${build}" --get-value name) + imgname=${osname}-${build}-layered-${name}.${basearch}.ociarchive + + # check if the image already exists in the meta.json + if [ -z "${force}" ]; then + path=$(cosa meta --build="${build}" --get-value "layered-images.${name}.path") + if [ "${path}" != "None" ]; then + echo "layered-${name} image already exists:" + echo "$imgname" + exit 0 + fi + unset path + fi + + builddir=$(get_build_dir "$build") + if [ ! -d "${builddir}" ]; then + fatal "Build dir ${builddir} does not exist." + fi + + oscontainer_meta_path=$(cosa meta --build="${build}" --get-value images.ostree.path) + oscontainer="builds/${build}/${basearch}/${oscontainer_meta_path}" + tmp_builddir="tmp/buildextend-layered-${name}" + rm -rf "${tmp_builddir}" && mkdir -p "${tmp_builddir}" + outfile="${tmp_builddir}/${imgname}" + + # A common pattern in the local developer path is to wrap `podman` so that + # it actually runs on the host. If privileged (proxy for "developer setup") + # and `podman info` works, use podman directly. + if has_privileges && podman info &>/dev/null; then + COSA_BUILDEXTEND_LAYERED_FORCE_INNER=1 cosa buildextend-layered "${name}" "${containerfile}" "${oscontainer}" "${outfile}" + else + cosa supermin-run /usr/lib/coreos-assembler/cmd-buildextend-layered "${name}" "${containerfile}" "${oscontainer}" "${outfile}" + fi + + # everything below is standard "add to meta.json and mv artifact to builddir" + + sha256=$(sha256sum_str < "${outfile}") + cosa meta --build "${build}" --dump | python3 -c " +import sys, json +j = json.load(sys.stdin) +if 'layered-images' not in j: + j['layered-images'] = {} +j['layered-images']['${name}'] = { + 'path': '${imgname}', + 'sha256': '${sha256}', + 'size': $(stat -c '%s' "${outfile}") +} +json.dump(j, sys.stdout, indent=4)" | jq -s add > "${tmp_builddir}/meta.json.new" + + cosa meta --build "${build}" --artifact "layered-${name}" --artifact-json "$(readlink -f "${tmp_builddir}/meta.json.new")" + /usr/lib/coreos-assembler/finalize-artifact "${outfile}" "${builddir}/${imgname}" + + rm -rf "${tmp_builddir}" +else + # This runs inside supermin (or still outside if + # COSA_BUILDEXTEND_LAYERED_FORCE_INNER is set) + + name=$1; shift + containerfile=$1; shift + oscontainer=$1; shift + outfile=$1; shift + + set -- + # we'll mount in the config and yumrepos dir + set -- "$@" --volume "$(pwd)/src/config":/run/src/config:ro + if [ -e src/yumrepos ]; then + set -- "$@" --volume "$(pwd)/src/yumrepos":/run/src/yumrepos:ro + fi + + # mount ca-trust if some repos need a custom root CA. + set -- "$@" --volume /etc/pki/ca-trust:/etc/pki/ca-trust:ro + + # we disable labeling to not require the mounts above to be container_file_t + set -- "$@" --security-opt label=disable + + podman build -t "localhost/cosa-layered-${name}" -f "${containerfile}" \ + --from oci-archive:"${oscontainer}" "$@" + skopeo copy containers-storage:"localhost/cosa-layered-${name}" oci-archive:"${outfile}" +fi diff --git a/src/v1.json b/src/v1.json index 53cf4b0936..46f155cdd4 100644 --- a/src/v1.json +++ b/src/v1.json @@ -224,6 +224,7 @@ "ibmcloud", "powervs", "images", + "layered-images", "koji", "oscontainer", "extensions", @@ -735,6 +736,18 @@ } } }, + "layered-images": { + "$id": "#/properties/layered-images", + "type": "object", + "title": "Layered Images", + "propertyNames": { + "pattern": "^[a-z][-a-z0-9]*$" + }, + "additionalProperties": { + "type": "object", + "$ref": "#/definitions/artifact" + } + }, "ostree-commit": { "$id": "#/properties/ostree-commit", "type": "string",