Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stages/ostree.deploy.container: allow deploying from container #1402

Merged
merged 4 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 33 additions & 22 deletions stages/org.osbuild.ostree.deploy
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ CAPABILITIES = ["CAP_MAC_ADMIN"]
SCHEMA_2 = """
"options": {
"additionalProperties": false,
"required": ["osname", "ref"],
"required": ["osname"],
dustymabe marked this conversation as resolved.
Show resolved Hide resolved
"properties": {
"mounts": {
"description": "Mount points of the final file system",
Expand Down Expand Up @@ -84,11 +84,12 @@ SCHEMA_2 = """
},
"inputs": {
"type": "object",
"required": ["commits"],
"additionalProperties": false,
"required": ["commits"],
"properties": {
"commits": {
"type": "object",
"description": "OStree commit to deploy",
"additionalProperties": true
}
}
Expand All @@ -104,44 +105,54 @@ def make_fs_identifier(desc):
raise ValueError("unknown rootfs type")


def main(tree, inputs, options):
osname = options["osname"]
rootfs = options.get("rootfs")
mounts = options.get("mounts", [])
kopts = options.get("kernel_opts", [])
ref = options["ref"]
remote = options.get("remote")
def ostree_commit_deploy(tree, inputs, osname, remote, ref, kopts):
if len(inputs) == 0:
if not ref:
achilleas-k marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError("ref should be specified in options")
elif len(inputs) == 1:
if ref:
raise ValueError("Should not specify ref if input was specified")

# If provided an input then do the pull into the tree
if len(inputs) != 0:
# If we have an input then we need to pull_local() from the input
# first before we deploy.
source_repo, commits = ostree.parse_input_commits(inputs["commits"])
target_repo = f"{tree}/ostree/repo"
for commit, data in commits.items():
loopref = data.get("ref", commit)
ostree.pull_local(source_repo, target_repo, remote, loopref)
ref = data.get("ref", commit)
ostree.pull_local(source_repo, target_repo, remote, ref)

if remote:
ref = f"{remote}:{ref}"
achilleas-k marked this conversation as resolved.
Show resolved Hide resolved

kargs = []
kargs = [f'--karg-append={v}' for v in kopts]
ostree.cli("admin", "deploy", ref,
*kargs, sysroot=tree, os=osname)


def main(tree, inputs, options):
osname = options["osname"]
rootfs = options.get("rootfs")
mounts = options.get("mounts", [])
kopts = options.get("kernel_opts", [])
ref = options.get("ref", "")
remote = options.get("remote")

# schema should catch the case in which there are more
# than one input but this adds a second layer of security
if len(inputs) > 1:
raise ValueError("Only one input accepted")

if rootfs:
rootfs_id = make_fs_identifier(rootfs)
kargs += [f"--karg=root={rootfs_id}"]

for opt in kopts:
kargs += [f"--karg-append={opt}"]
kopts += [f"root={rootfs_id}"]

with MountGuard() as mounter:
for mount in mounts:
path = mount.lstrip("/")
path = os.path.join(tree, path)
mounter.mount(path, path)

ostree.cli("admin", "deploy", ref,
*kargs,
sysroot=tree,
os=osname)
ostree_commit_deploy(tree, inputs, osname, remote, ref, kopts)


if __name__ == '__main__':
Expand Down
143 changes: 143 additions & 0 deletions stages/org.osbuild.ostree.deploy.container
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/usr/bin/python3
"""
Deploy an OStree commit

Create an OSTree deployment[1] for a given container image input
"""

import os
import sys

import osbuild.api
from osbuild.util import containers, ostree
from osbuild.util.mnt import MountGuard

CAPABILITIES = ["CAP_MAC_ADMIN"]

SCHEMA_2 = """
"options": {
"additionalProperties": false,
"required": ["osname", "target_imgref"],
"properties": {
"mounts": {
"description": "Mount points of the final file system",
"type": "array",
"items": {
"description": "Description of one mount point",
"type": "string"
}
},
"osname": {
"description": "Name of the stateroot to be used in the deployment",
"type": "string"
},
"kernel_opts": {
"description": "Additional kernel command line options",
"type": "array",
"items": {
"description": "A single kernel command line option",
"type": "string"
}
},
"target_imgref": {
"description": "imageref used as the source of truth for updates",
"type": "string",
"pattern": "^(ostree-remote-registry|ostree-image-signed|ostree-unverified-registry):.*$",
"examples": ["ostree-remote-registry:fedora:quay.io/fedora/fedora-coreos:stable, ostree-image-signed:quay.io/fedora/fedora-coreos:stable, ostree-unverified-registry:quay.io/fedora/fedora-coreos:stable"]
},
"rootfs": {
"description": "Identifier to locate the root file system",
"type": "object",
"oneOf": [{
"required": ["uuid"]
}, {
"required": ["label"]
}],
"properties": {
"label": {
"description": "Identify the root file system by label",
"type": "string"
},
"uuid": {
"description": "Identify the root file system by UUID",
"type": "string"
}
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["images"],
"properties": {
"images": {
"type": "object",
"description": "Container Image to deploy",
"additionalProperties": true
}
}
}
"""


def make_fs_identifier(desc):
for key in ["uuid", "label"]:
val = desc.get(key)
if val:
return f"{key.upper()}={val}"
raise ValueError("unknown rootfs type")


def ostree_container_deploy(tree, inputs, osname, target_imgref, kopts):
images = containers.parse_containers_input(inputs)
for image in images.values():
with containers.container_source(image) as (_, image_source):
extra_args = []
imgref = f"ostree-unverified-image:{image_source}"

extra_args.append(f'--imgref={imgref}')
extra_args.append(f'--stateroot={osname}')

# consider implicit signature verification type checks, but
# can't think of a "clean" way to do it yet other than
# parsing the target-imgref and separating by the ':' character
extra_args.append(f'--target-imgref={target_imgref}')

kargs = [f'--karg={v}' for v in kopts]

ostree.cli("container", "image", "deploy",
*extra_args, sysroot=tree, *kargs)


def main(tree, inputs, options):
osname = options["osname"]
rootfs = options.get("rootfs")
mounts = options.get("mounts", [])
kopts = options.get("kernel_opts", [])
target_imgref = options.get("target_imgref")

# schema should catch the case in which there are more
# than one input but this adds a second layer of security
if len(inputs) > 1:
raise ValueError("Only one input accepted")

if rootfs:
rootfs_id = make_fs_identifier(rootfs)
kopts += [f"root={rootfs_id}"]

with MountGuard() as mounter:
for mount in mounts:
path = mount.lstrip("/")
path = os.path.join(tree, path)
mounter.mount(path, path)

ostree_container_deploy(tree, inputs, osname, target_imgref, kopts)


if __name__ == '__main__':
stage_args = osbuild.api.arguments()
r = main(stage_args["tree"],
stage_args["inputs"],
stage_args["options"])
sys.exit(r)
8 changes: 8 additions & 0 deletions test/cases/ostree-images
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ def run_tests(args, tmpdir):
"artifact": "disk.qcow2"
}
},
},
"fedora-coreos-container": {
"manifest": "fedora-coreos-container.json",
"exports": {
"qcow2": {
"artifact": "disk.qcow2"
}
},
}
}

Expand Down
Loading