|
| 1 | +#!/usr/bin/python3 -u |
| 2 | +# Add content on top of a commit, handling SELinux labeling etc. |
| 3 | +# This is a more flexible (and correct) replacement for the CI flow developed |
| 4 | +# in https://github.com/smarterclayton/origin/blob/4de957b019aee56931b1a29af148cf64865a969b/images/os/Dockerfile |
| 5 | +# Down the line we want to lower this into rpm-ostree (and support RPMs too). |
| 6 | +# This script though should be very convenient for the common case of |
| 7 | +# overlaying some new binaries into /usr/bin that don't need any scripts |
| 8 | +# run and also don't need e.g. to regenerate the initramfs that would |
| 9 | +# happen in a full `rpm-ostree compose tree`. |
| 10 | +# Also related to the initramfs, this script explicitly doesn't |
| 11 | +# use or require any container features, so it will work inside |
| 12 | +# an podman/Kube container without special privileges. |
| 13 | + |
| 14 | +import argparse |
| 15 | +import gi |
| 16 | +import sys, os |
| 17 | +import tempfile |
| 18 | +import subprocess |
| 19 | + |
| 20 | +gi.require_version('OSTree', '1.0') |
| 21 | +gi.require_version('Json', '1.0') |
| 22 | +from gi.repository import GLib, Gio, OSTree, Json |
| 23 | + |
| 24 | +# This seems to not be exposed by Python which has its own wrappers, |
| 25 | +# It's also not exposed by GLib. This is a Linux specific value |
| 26 | +# but that's totally fine, OSTree only works there too. |
| 27 | +AT_FDCWD = -100 |
| 28 | + |
| 29 | +parser = argparse.ArgumentParser() |
| 30 | +parser.add_argument("--repo", help="repo", required=True) |
| 31 | +parser.add_argument("--rev", help="Revision to override") |
| 32 | +#parser.add_argument("--add-rpm", help="Unpack literal RPM content (does not currently update rpm DB, replace older RPM, or run any scripts)") |
| 33 | +parser.add_argument("--add-tree", help="Add local filesystem tree", default=[], action="append") |
| 34 | +parser.add_argument("--output-ref", help="Output ref (if unset, will just write new commit)") |
| 35 | +args = parser.parse_args() |
| 36 | + |
| 37 | +repo = OSTree.Repo.new(Gio.File.new_for_path(args.repo)) |
| 38 | +repo.open(None) |
| 39 | + |
| 40 | +if args.rev is not None: |
| 41 | + rev = args.rev |
| 42 | +else: |
| 43 | + [_,refs] = repo.list_refs(None, None) |
| 44 | + nrefs = len(refs) |
| 45 | + if nrefs > 1: |
| 46 | + raise SystemExit(f"No rev specified and repo has {nrefs} refs") |
| 47 | + rev = refs[0] |
| 48 | +[_,root,rev] = repo.read_commit(rev, None) |
| 49 | +[_, base_commit, _] = repo.load_commit(rev) |
| 50 | +# See https://github.com/ostreedev/ostree/pull/1643 |
| 51 | +base_contents_checksum = OSTree.checksum_from_bytes_v(base_commit.get_child_value(6)) |
| 52 | +base_meta_checksum = OSTree.checksum_from_bytes_v(base_commit.get_child_value(7)) |
| 53 | +mtree = OSTree.MutableTree.new_from_checksum(repo, base_contents_checksum, base_meta_checksum) |
| 54 | + |
| 55 | +tmpd = tempfile.TemporaryDirectory(dir=f"{args.repo}/tmp/", prefix="dev-overlay") |
| 56 | +def add_commit_filter(repo, path, finfo): |
| 57 | + # Canonicalize uid/gid to 0 |
| 58 | + finfo.set_attribute_uint32("unix::uid", 0) |
| 59 | + finfo.set_attribute_uint32("unix::gid", 0) |
| 60 | + return OSTree.RepoCommitFilterResult.ALLOW |
| 61 | +add_modifier = OSTree.RepoCommitModifier.new(OSTree.RepoCommitModifierFlags.DEVINO_CANONICAL | |
| 62 | + OSTree.RepoCommitModifierFlags.SKIP_XATTRS, add_commit_filter) |
| 63 | +if root.get_child("usr/etc/selinux"): |
| 64 | + opts = OSTree.RepoCheckoutAtOptions() |
| 65 | + opts.mode = OSTree.RepoCheckoutMode.USER |
| 66 | + opts.subpath = "/usr/etc/selinux" |
| 67 | + dest = tmpd.name + "/" + opts.subpath |
| 68 | + os.makedirs(os.path.dirname(dest)) |
| 69 | + repo.checkout_at(opts, AT_FDCWD, dest, rev, None) |
| 70 | + add_modifier.set_sepolicy(OSTree.SePolicy.new(Gio.File.new_for_path(tmpd.name))) |
| 71 | +for d in args.add_tree: |
| 72 | + repo.write_dfd_to_mtree(AT_FDCWD, d, mtree, add_modifier, None) |
| 73 | + |
| 74 | +[_,dir_tree] = repo.write_mtree(mtree, None) |
| 75 | +[_,new_commit] = repo.write_commit(None, None, None, None, dir_tree, None) |
| 76 | +if args.output_ref is not None: |
| 77 | + repo.set_ref_immediate(None, args.output_ref, new_commit) |
| 78 | + print(f"Wrote {args.output_ref} => {new_commit}") |
| 79 | +else: |
| 80 | + print(f"Wrote {new_commit}") |
0 commit comments