From b6851821e2dbab7e258023c8db7bedc737837931 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 | 148 ++++++++++++++++++++++++++++++++++++ src/v1.json | 13 ++++ 5 files changed, 178 insertions(+), 3 deletions(-) create mode 100755 src/cmd-buildextend-layered diff --git a/cmd/coreos-assembler.go b/cmd/coreos-assembler.go index d7f14a7976..3c15f37d38 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", "upload-oscontainer", "buildextend-extensions"} -var buildextendCommands = []string{"aliyun", "applehv", "aws", "azure", "digitalocean", "exoscale", "extensions", "extensions-container", "gcp", "hashlist-experimental", "hyperv", "ibmcloud", "kubevirt", "legacy-oscontainer", "live", "metal", "metal4k", "nutanix", "openstack", "qemu", "secex", "virtualbox", "vmware", "vultr"} +var buildextendCommands = []string{"aliyun", "applehv", "aws", "azure", "digitalocean", "exoscale", "extensions", "extensions-container", "gcp", "hashlist-experimental", "hyperv", "ibmcloud", "kubevirt", "layered", "legacy-oscontainer", "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..402379abcf --- /dev/null +++ b/src/cmd-buildextend-layered @@ -0,0 +1,148 @@ +#!/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 + + # We disable labeling here to not require the srcconfig and yumrepos dir + # to be container_file_t. Also mount ca-trust if some repos need a custom + # root CA. + podman build -t "localhost/cosa-layered-${name}" -f "${containerfile}" \ + --from oci-archive:"${oscontainer}" --security-opt label=disable \ + --volume "$(pwd)/src/config":/run/src/config:ro \ + --volume "$(pwd)/src/yumrepos":/run/src/yumrepos:ro \ + --volume /etc/pki/ca-trust:/etc/pki/ca-trust:ro + 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",