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: add aleph file #10

Closed
2 changes: 1 addition & 1 deletion osbuild.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%global forgeurl https://github.com/osbuild/osbuild
%global selinuxtype targeted

Version: 98
Version: 99

%forgemeta

Expand Down
2 changes: 1 addition & 1 deletion osbuild/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from .pipeline import Manifest, Pipeline, Stage

__version__ = "98"
__version__ = "99"

__all__ = [
"Manifest",
Expand Down
2 changes: 1 addition & 1 deletion osbuild/buildroot.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def run(self, argv, monitor, timeout=None, binds=None, readonly_binds=None, extr

# Import directories from the caller-provided root.
imports = ["usr"]
if self.mount_boot:
if True:
lukewarmtemp marked this conversation as resolved.
Show resolved Hide resolved
imports.insert(0, "boot")

for p in imports:
Expand Down
8 changes: 8 additions & 0 deletions osbuild/objectstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,18 @@ def init(self):
# with just /usr mounted from the host
usr = os.path.join(root, "usr")
os.makedirs(usr)
boot = os.path.join(root, "boot")
os.makedirs(boot)
etc = os.path.join(root, "etc")
os.makedirs(etc)

# ensure / is read-only
mount(root, root)
mount("/etc", etc)
mount("/usr", usr)
mount("/boot", boot)



@property
def tree(self) -> os.PathLike:
Expand Down
43 changes: 39 additions & 4 deletions osbuild/util/ostree.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import typing
# pylint doesn't understand the string-annotation below
from typing import Any, List # pylint: disable=unused-import
import glob

from osbuild.util.rhsm import Subscriptions

Expand Down Expand Up @@ -192,18 +193,16 @@ def pull_local(source_repo: PathLike, target_repo: PathLike, remote: str, ref: s
*extra_args,
repo=target_repo)


def cli(*args, _input=None, **kwargs):
"""Thin wrapper for running the ostree CLI"""
args = list(args) + [f'--{k}={v}' for k, v in kwargs.items()]
print("ostree " + " ".join(args), file=sys.stderr)
subprocess.run(["ostree"] + args,
return subprocess.run(["ostree"] + args,
encoding="utf8",
stdout=sys.stderr,
stdout=subprocess.PIPE,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh. I hadn't realized we'd need to make this change.. I honestly think this is the way it should be, but the osbuild devs might want stdout to still go to stderr to keep the same behavior. We can still achieve that goal if they ask us by just doing:

cp = subprocess.run(["ostree"] + args,
                   encoding="utf8",
                   stdout=sys.stderr,
                   stdout=subprocess.PIPE,\
                   input=_input,
                   check=True)
print(cp.stdout)
return cp

but let's wait and see what they say.

input=_input,
check=True)


def parse_input_commits(commits):
"""Parse ostree input commits and return the repo path and refs specified"""
data = commits["data"]
Expand All @@ -215,6 +214,14 @@ def parse_input_commits(commits):
def deployment_path(root: PathLike, osname: str, ref: str, serial: int):
"""Return the path to a deployment given the parameters"""

if osname == "" and ref == "" and serial == None:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there even a use case in osbuild for having multiple deployments in the image? I'd just nuke those parameters and always do the glob approach. That should simplify some of the callers currently doing extra work to prepare those params.

Obviously fine to do as a follow-up.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I think there is an argument to make for that - +1 for a follow up. We'd need to keep supporting specifying the options even though they would do nothing.

filenames = glob.glob(root + '/ostree/deploy/*/deploy/*.0', recursive=True)
if len(filenames) < 1:
raise ValueError("Could not find deployment")
elif len(filenames) > 1:
raise ValueError("More than one deployment found")
return filenames[0]

base = os.path.join(root, "ostree")

repo = os.path.join(base, "repo")
Expand All @@ -224,6 +231,34 @@ def deployment_path(root: PathLike, osname: str, ref: str, serial: int):
sysroot = f"{stateroot}/deploy/{commit}.{serial}"

return sysroot


def parse_origin(origin: PathLike):
"""Parse the origin file and return the deployment type and imgref

Example container case: container-image-reference=ostree-remote-image:fedora:docker://quay.io/fedora/fedora-coreos:stable
Example ostree commit case: refspec=fedora:fedora/x86_64/coreos/stable
"""
lukewarmtemp marked this conversation as resolved.
Show resolved Hide resolved
deploy_type = ""
imgref = ""
with open(origin) as f:
for line in f:
separated_line = line.split("=")
if separated_line[0] == "container-image-reference":
deploy_type = "container"
imgref = separated_line[1].rstrip()
break
elif separated_line[0] == "refspec":
deploy_type = "ostree_commit"
imgref = separated_line[1].rstrip()
break

if deploy_type == "":
raise ValueError("Could not find 'container-image-reference' or 'refspec' in origin file")
elif imgref == "":
raise ValueError("Could not find imgref in origin file")

return deploy_type, imgref


class PasswdLike:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setuptools.setup(
name="osbuild",
version="98",
version="99",
description="A build system for OS images",
packages=["osbuild", "osbuild.formats", "osbuild.util"],
license='Apache-2.0',
Expand Down
166 changes: 166 additions & 0 deletions stages/org.osbuild.ostree.aleph
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#!/usr/bin/python3
"""
Create aleph version file for the deployment.
"""


import os
import sys

import osbuild.api
from osbuild.util import ostree

import json

CAPABILITIES = ["CAP_MAC_ADMIN"]
ALEPH_FILENAME = ".aleph-version.json"
COREOS_ALEPH_FILENAME = ".coreos-aleph-version.json"


SCHEMA_2 = """
"options": {
"additionalProperties": false,
"properties": {
"coreos_compat": {
"description": "boolean to allow for CoreOS aleph version backwards compatibility",
"type": "boolean"
},
"deployment": {
"additionalProperties": false,
"required": ["osname", "ref"],
"properties": {
"osname": {
"description": "Name of the stateroot to be used in the deployment",
"type": "string"
},
"ref": {
"description": "OStree ref to create and use for deployment",
"type": "string"
},
"serial": {
"description": "The deployment serial (usually '0')",
"type": "number",
"default": 0
}
}
}
}
}
"""


def aleph_commit(tree, imgref):
extra_args = []
extra_args.append("--print-metadata-key=version")

aleph_version = ostree.cli("show", f"--repo={tree}/ostree/repo", imgref, *extra_args).stdout.rstrip().strip('\'')
aleph_ref = imgref
# get the commit by parsing the revision of the deployment
aleph_ostree_commit = ostree.rev_parse(tree + "/ostree/repo", imgref)

aleph_version_data = {
"osbuild-version": osbuild.__version__,
"version": aleph_version,
"ref": aleph_ref,
"ostree-commit": aleph_ostree_commit
}

return aleph_version_data


def aleph_container(tree, imgref):
# extract the image name from the imgref
imgref_list = imgref.split(':')
if imgref_list[0] in ["ostree-remote-registry", "ostree-remote-image"]:
img_name = ':'.join(imgref_list[2:])
elif imgref_list[0] in ["ostree-image-signed", "ostree-unverified-registry"]:
img_name = ':'.join(imgref_list[1:])
else:
raise ValueError(f"Image ref {imgref} has unsupported type (supported: 'ostree-remote-registry', \
'ostree-remote-image', 'ostree-image-signed', or 'ostree-unverified-registry')")

img_name = img_name.lstrip('docker://')

extra_args = []
extra_args.append(f"--repo={tree}/ostree/repo")
extra_args.append(f"registry:{img_name}")

container_data_json = ostree.cli("container", "image", "metadata", *extra_args).stdout.rstrip()
container_data = json.loads(container_data_json)

extra_args.append("--config")
container_data_config_json = ostree.cli("container", "image", "metadata", *extra_args).stdout.rstrip()
container_data_config = json.loads(container_data_config_json)

aleph_digest = container_data['config']['digest']
aleph_ref = f"docker://{imgref}"
aleph_version = container_data_config['config']['Labels']['org.opencontainers.image.version']
aleph_container_image = container_data_config['config']['Labels']

aleph_version_data = {
"osbuild-version": osbuild.__version__,
"ref": aleph_ref,
"version": aleph_version,
"container-image": {
"image-name": img_name,
"image-digest": aleph_digest,
"image-labels": aleph_container_image
}
}

# the 'ostree.commit' label will be optional in the future so
# prevent hard failing if key is not found
aleph_ostree_commit = container_data_config['config']['Labels'].get('ostree.commit')
if aleph_ostree_commit is not None:
aleph_version_data["ostree-commit"] = aleph_ostree_commit

return aleph_version_data


def construct_aleph_json(tree, origin):
deploy_type, imgref = ostree.parse_origin(origin)
data = {}
# null deploy_type and imgref error is caught in the parse_origin() function
if deploy_type == "container":
data = aleph_container(tree, imgref)
elif deploy_type == "ostree_commit":
data = aleph_commit(tree, imgref)
lukewarmtemp marked this conversation as resolved.
Show resolved Hide resolved
else:
raise ValueError("Unknown deployment type")

return data


def main(tree, options):
coreos_compat = options.get("coreos_compat", False)
dep = options.get("deployment", None)
osname = ""
ref = ""
serial = None
origin = ""

# if deployment is specified, use it to find the origin file.
# otherwise, autodetect the only deployment found in the tree.
if dep is not None:
osname = dep.get("osname", "")
ref = dep.get("ref", "")
serial = dep.get("serial", 0)

origin = ostree.deployment_path(tree, osname, ref, serial) + ".origin"
data = construct_aleph_json(tree, origin)

# write the data out to the file
with open(os.path.join(tree, ALEPH_FILENAME), "w") as f:
json.dump(data, f, indent=4, sort_keys=True)
f.write("\n")

# create a symlink for backwards compatibility with CoreOS
if coreos_compat:
os.symlink(ALEPH_FILENAME, os.path.join(tree, COREOS_ALEPH_FILENAME))


if __name__ == '__main__':
stage_args = osbuild.api.arguments()
r = main(stage_args["tree"],
stage_args["options"])
sys.exit(r)
44 changes: 38 additions & 6 deletions test/data/manifests/fedora-coreos-container.mpp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,22 @@ mpp-define-image:
size: 4194304
type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4
uuid: CA7D7CCB-63ED-4C53-861C-1742536059CC
# sources:
# org.osbuild.ostree:
# items:
# 2c39e9b6a6b26226b626f4daf815d5307a96b2cd57463e94f7c618e312a61980:
# remote:
# url: https://kojipkgs.fedoraproject.org/ostree/repo/
pipelines:
- mpp-import-pipelines:
path: fedora-vars.ipp.yaml
- mpp-import-pipeline:
path: fedora-build-v2.ipp.yaml
id: build
# - mpp-import-pipeline:
# path: fedora-build-v2.ipp.yaml
# id: build
runner:
mpp-format-string: org.osbuild.fedora{release}
- name: image-tree
build: name:build
# build: name:build
source-epoch: 1659397331
stages:
- type: org.osbuild.ostree.init-fs
Expand Down Expand Up @@ -75,6 +81,32 @@ pipelines:
images:
- source: registry.gitlab.com/redhat/services/products/image-builder/ci/images/fedora-coreos
tag: stable
- type: org.osbuild.ostree.aleph
options:
coreos_compat: true
# - type: org.osbuild.ostree.deploy
# options:
# osname: fedora-coreos
# remote: fedora
# mounts:
# - /boot
# - /boot/efi
# kernel_opts:
# - rw
# - console=tty0
# - console=ttyS0
# - ignition.platform.id=qemu
# - '$ignition_firstboot'
# inputs:
# commits:
# type: org.osbuild.ostree
# origin: org.osbuild.source
# references:
# "2c39e9b6a6b26226b626f4daf815d5307a96b2cd57463e94f7c618e312a61980":
# ref: fedora/x86_64/coreos/stable
# - type: org.osbuild.ostree.aleph
# options:
# coreos_compat: true
- type: org.osbuild.ostree.selinux
options:
deployment:
Expand All @@ -94,7 +126,7 @@ pipelines:
greenboot: false
ignition: true
- name: image
build: name:build
# build: name:build
stages:
- type: org.osbuild.truncate
options:
Expand Down Expand Up @@ -218,7 +250,7 @@ pipelines:
mpp-format-int: '{image.layout[''boot''].index}'
path: /grub2
- name: qcow2
build: name:build
# build: name:build
stages:
- type: org.osbuild.qemu
inputs:
Expand Down