ostree: add convenience function for using default OSTree deployment
This adds a `default: true` option for all cases where OSTree information is specified in schemas and allows for the information to be picked up from the filesystem. This is a safe operation because when building disk images there is no known case where having two deployments makes sense. In the case there ever were a case then the osname, ref, and serial options still exist and can be used. Co-authored-by: Luke Yang <luyang@redhat.com> Co-authored-by: Michael Vogt <michael.vogt@gmail.com>
This commit is contained in:
parent
2021b915f1
commit
e1cbf92673
11 changed files with 264 additions and 49 deletions
|
|
@ -41,7 +41,26 @@ SCHEMA_2 = """
|
|||
"deployment": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["osname", "ref"],
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [false]}
|
||||
},
|
||||
"required": ["osname", "ref"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [true]}
|
||||
},
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{"required": ["osname"]},
|
||||
{"required": ["ref"]},
|
||||
{"required": ["serial"]}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"osname": {
|
||||
"description": "Name of the stateroot to be used in the deployment",
|
||||
|
|
@ -55,6 +74,11 @@ SCHEMA_2 = """
|
|||
"description": "The deployment serial (usually '0')",
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"default": {
|
||||
"description": "Find and use the default ostree deployment",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,12 +123,9 @@ class OSTreeDeploymentMount(mounts.MountService):
|
|||
tree = args["tree"]
|
||||
mountroot = args["root"]
|
||||
options = args["options"]
|
||||
|
||||
source = options.get("source", "tree")
|
||||
deployment = options["deployment"]
|
||||
osname = deployment["osname"]
|
||||
ref = deployment["ref"]
|
||||
serial = deployment.get("serial", 0)
|
||||
osname, ref, serial = ostree.parse_deployment_option(tree, deployment)
|
||||
|
||||
# The user could specify either the tree or mountroot as the
|
||||
# place where we want the deployment to be mounted.
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import collections
|
||||
import contextlib
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import typing
|
||||
# pylint doesn't understand the string-annotation below
|
||||
from typing import Any, List # pylint: disable=unused-import
|
||||
from typing import Any, Dict, List, Tuple # pylint: disable=unused-import
|
||||
|
||||
from osbuild.util.rhsm import Subscriptions
|
||||
|
||||
|
|
@ -214,7 +216,43 @@ def parse_input_commits(commits):
|
|||
return commits["path"], data["refs"]
|
||||
|
||||
|
||||
def deployment_path(root: PathLike, osname: str, ref: str, serial: int):
|
||||
def parse_deployment_option(root: PathLike, deployment: Dict) -> Tuple[str, str, str]:
|
||||
"""Parse the deployment option and return the osname, ref, and serial
|
||||
|
||||
The `deployment` arg contains the following sub fields:
|
||||
- osname: Name of the stateroot used in the deployment (ie. fedora-coreos)
|
||||
- ref: OStree ref to used for the deployment (ie. fedora/aarch64/coreos/next)
|
||||
- serial: The deployment serial (ie. 0)
|
||||
- default: Boolean to determine whether the default ostree deployment should be used
|
||||
"""
|
||||
|
||||
default_deployment = deployment.get("default")
|
||||
if default_deployment:
|
||||
filenames = glob.glob(os.path.join(root, 'ostree/deploy/*/deploy/*.0'))
|
||||
if len(filenames) < 1:
|
||||
raise ValueError("Could not find deployment")
|
||||
if len(filenames) > 1:
|
||||
raise ValueError(f"More than one deployment found: {filenames}")
|
||||
|
||||
# We pick up the osname, commit, and serial from the filesystem
|
||||
# here. We'll return the detected commit as the ref in this
|
||||
# since it's a valid substitute for all subsequent uses in
|
||||
# the code base.
|
||||
f = re.search("/ostree/deploy/(.*)/deploy/(.*)\\.([0-9])", filenames[0])
|
||||
if not f:
|
||||
raise ValueError("cannot find ostree deployment in {filenames[0]}")
|
||||
osname = f.group(1)
|
||||
commit = f.group(2)
|
||||
serial = f.group(3)
|
||||
return osname, commit, serial
|
||||
|
||||
osname = deployment["osname"]
|
||||
ref = deployment["ref"]
|
||||
serial = deployment.get("serial", 0)
|
||||
return osname, ref, serial
|
||||
|
||||
|
||||
def deployment_path(root: PathLike, osname: str = "", ref: str = "", serial: int = 0):
|
||||
"""Return the path to a deployment given the parameters"""
|
||||
|
||||
base = os.path.join(root, "ostree")
|
||||
|
|
|
|||
|
|
@ -32,7 +32,26 @@ SCHEMA_2 = r"""
|
|||
"deployment": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["osname", "ref"],
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [false]}
|
||||
},
|
||||
"required": ["osname", "ref"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [true]}
|
||||
},
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{"required": ["osname"]},
|
||||
{"required": ["ref"]},
|
||||
{"required": ["serial"]}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"osname": {
|
||||
"description": "Name of the stateroot to be used in the deployment",
|
||||
|
|
@ -46,6 +65,11 @@ SCHEMA_2 = r"""
|
|||
"description": "The deployment serial (usually '0')",
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"default": {
|
||||
"description": "Find and use the default ostree deployment",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -101,9 +125,7 @@ def main(args, options):
|
|||
# we'll call ostree.deployment_path() helper to find it for us.
|
||||
root = mounts
|
||||
if deployment:
|
||||
osname = deployment["osname"]
|
||||
ref = deployment["ref"]
|
||||
serial = deployment.get("serial", 0)
|
||||
osname, ref, serial = ostree.parse_deployment_option(mounts, deployment)
|
||||
root = ostree.deployment_path(mounts, osname, ref, serial)
|
||||
|
||||
bootupd_args = []
|
||||
|
|
|
|||
|
|
@ -29,7 +29,26 @@ SCHEMA = """
|
|||
"deployment": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["osname","ref"],
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [false]}
|
||||
},
|
||||
"required": ["osname", "ref"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [true]}
|
||||
},
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{"required": ["osname"]},
|
||||
{"required": ["ref"]},
|
||||
{"required": ["serial"]}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"osname": {
|
||||
"description": "Name of the stateroot to be used in the deployment",
|
||||
|
|
@ -43,6 +62,11 @@ SCHEMA = """
|
|||
"description": "The deployment serial (usually '0')",
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"default": {
|
||||
"description": "Find and use the default ostree deployment",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,9 +142,7 @@ def main(tree, options):
|
|||
|
||||
if ostree_options:
|
||||
deployment = ostree_options["deployment"]
|
||||
osname = deployment["osname"]
|
||||
ref = deployment["ref"]
|
||||
serial = deployment.get("serial", 0)
|
||||
osname, ref, serial = ostree.parse_deployment_option(tree, deployment)
|
||||
|
||||
root = ostree.deployment_path(tree, osname, ref, serial)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ COREOS_ALEPH_FILENAME = ".coreos-aleph-version.json"
|
|||
SCHEMA_2 = """
|
||||
"options": {
|
||||
"additionalProperties": false,
|
||||
"required": ["deployment"],
|
||||
"properties": {
|
||||
"coreos_compat": {
|
||||
"description": "boolean to allow for CoreOS aleph version backwards compatibility",
|
||||
|
|
@ -26,7 +27,26 @@ SCHEMA_2 = """
|
|||
},
|
||||
"deployment": {
|
||||
"additionalProperties": false,
|
||||
"required": ["osname", "ref"],
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [false]}
|
||||
},
|
||||
"required": ["osname", "ref"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [true]}
|
||||
},
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{"required": ["osname"]},
|
||||
{"required": ["ref"]},
|
||||
{"required": ["serial"]}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"osname": {
|
||||
"description": "Name of the stateroot to be used in the deployment",
|
||||
|
|
@ -40,6 +60,11 @@ SCHEMA_2 = """
|
|||
"description": "The deployment serial (usually '0')",
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"default": {
|
||||
"description": "Find and use the default ostree deployment",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,9 +156,7 @@ def construct_aleph_json(tree, origin):
|
|||
def main(tree, options):
|
||||
coreos_compat = options.get("coreos_compat", False)
|
||||
dep = options["deployment"]
|
||||
osname = dep["osname"]
|
||||
ref = dep["ref"]
|
||||
serial = dep.get("serial", 0)
|
||||
osname, ref, serial = ostree.parse_deployment_option(tree, dep)
|
||||
|
||||
origin = ostree.deployment_path(tree, osname, ref, serial) + ".origin"
|
||||
data = construct_aleph_json(tree, origin)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,26 @@ SCHEMA = """
|
|||
"properties": {
|
||||
"deployment": {
|
||||
"additionalProperties": false,
|
||||
"required": ["osname", "ref"],
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [false]}
|
||||
},
|
||||
"required": ["osname", "ref"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [true]}
|
||||
},
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{"required": ["osname"]},
|
||||
{"required": ["ref"]},
|
||||
{"required": ["serial"]}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"osname": {
|
||||
"description": "Name of the stateroot to be used in the deployment",
|
||||
|
|
@ -32,6 +51,11 @@ SCHEMA = """
|
|||
"description": "The deployment serial (usually '0')",
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"default": {
|
||||
"description": "Find and use the default ostree deployment",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -71,9 +95,7 @@ def populate_var(sysroot):
|
|||
|
||||
def main(tree, options):
|
||||
dep = options["deployment"]
|
||||
osname = dep["osname"]
|
||||
ref = dep["ref"]
|
||||
serial = dep.get("serial", 0)
|
||||
osname, ref, serial = ostree.parse_deployment_option(tree, dep)
|
||||
|
||||
deployment = ostree.deployment_path(tree, osname, ref, serial)
|
||||
var = os.path.join(tree, "ostree", "deploy", osname, "var")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,26 @@ SCHEMA = """
|
|||
"properties": {
|
||||
"deployment": {
|
||||
"additionalProperties": false,
|
||||
"required": ["osname", "ref"],
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [false]}
|
||||
},
|
||||
"required": ["osname", "ref"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"default": {"enum": [true]}
|
||||
},
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{"required": ["osname"]},
|
||||
{"required": ["ref"]},
|
||||
{"required": ["serial"]}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"osname": {
|
||||
"description": "Name of the stateroot to be used in the deployment",
|
||||
|
|
@ -35,6 +54,11 @@ SCHEMA = """
|
|||
"description": "The deployment serial (usually '0')",
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"default": {
|
||||
"description": "Find and use the default ostree deployment",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,9 +68,7 @@ SCHEMA = """
|
|||
|
||||
def main(tree, options):
|
||||
dep = options["deployment"]
|
||||
osname = dep["osname"]
|
||||
ref = dep["ref"]
|
||||
serial = dep.get("serial", 0)
|
||||
osname, ref, serial = ostree.parse_deployment_option(tree, dep)
|
||||
|
||||
# this created a state root at `osname`
|
||||
stateroot = f"{tree}/ostree/deploy/{osname}"
|
||||
|
|
|
|||
|
|
@ -12,10 +12,17 @@ STAGE_NAME = "org.osbuild.bootupd"
|
|||
@pytest.mark.parametrize("test_data,expected_err", [
|
||||
# bad
|
||||
({"deployment": "must-be-object"}, "'must-be-object' is not of type 'object'"),
|
||||
({"deployment": {"osname": "some-os"}}, "'ref' is a required property"),
|
||||
({"deployment": {"ref": "some-ref"}}, "'osname' is a required property"),
|
||||
({"deployment": {"osname": "some-os"}}, "{'osname': 'some-os'} is not valid under any of the given schemas"),
|
||||
({"deployment": {"ref": "some-ref"}}, "{'ref': 'some-ref'} is not valid under any of the given schemas"),
|
||||
({"deployment": {"osname": "some-os", "ref": "some-ref", "serial": "must-be-number"}},
|
||||
"'must-be-number' is not of type 'number'"),
|
||||
({"deployment": {"default": False}}, "{'default': False} is not valid under any of the given schemas"),
|
||||
({"deployment": {
|
||||
"osname": "some-os",
|
||||
"ref": "some-ref",
|
||||
"serial": 0,
|
||||
"default": True}
|
||||
}, "{'osname': 'some-os', 'ref': 'some-ref', 'serial': 0, 'default': True} is not valid under any of the given schemas"),
|
||||
({"random": "property"}, "Additional properties are not allowed"),
|
||||
({"bios": {}}, "'device' is a required property"),
|
||||
({"bios": "must-be-object"}, "'must-be-object' is not of type 'object'"),
|
||||
|
|
@ -33,6 +40,17 @@ STAGE_NAME = "org.osbuild.bootupd"
|
|||
{
|
||||
"device": "/dev/sda",
|
||||
},
|
||||
}, ""),
|
||||
({
|
||||
"deployment":
|
||||
{
|
||||
"default": True,
|
||||
},
|
||||
"static-configs": True,
|
||||
"bios":
|
||||
{
|
||||
"device": "/dev/sda",
|
||||
},
|
||||
}, "")
|
||||
|
||||
])
|
||||
|
|
|
|||
|
|
@ -518,8 +518,7 @@
|
|||
"options": {
|
||||
"coreos_compat": true,
|
||||
"deployment": {
|
||||
"ref": "ostree/1/1/0",
|
||||
"osname": "fedora-coreos"
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -527,8 +526,7 @@
|
|||
"type": "org.osbuild.ostree.selinux",
|
||||
"options": {
|
||||
"deployment": {
|
||||
"ref": "ostree/1/1/0",
|
||||
"osname": "fedora-coreos"
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -705,8 +703,7 @@
|
|||
},
|
||||
"static-configs": true,
|
||||
"deployment": {
|
||||
"ref": "ostree/1/1/0",
|
||||
"osname": "fedora-coreos"
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
|
|
@ -956,8 +953,7 @@
|
|||
"options": {
|
||||
"static-configs": true,
|
||||
"deployment": {
|
||||
"ref": "ostree/1/1/0",
|
||||
"osname": "fedora-coreos"
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
|
|
|
|||
|
|
@ -129,15 +129,11 @@ pipelines:
|
|||
options:
|
||||
coreos_compat: true
|
||||
deployment:
|
||||
ref: ostree/1/1/0
|
||||
osname:
|
||||
mpp-format-string: '{osname}'
|
||||
default: true
|
||||
- type: org.osbuild.ostree.selinux
|
||||
options:
|
||||
deployment:
|
||||
ref: ostree/1/1/0
|
||||
osname:
|
||||
mpp-format-string: '{osname}'
|
||||
default: true
|
||||
- name: raw-image
|
||||
build: name:build
|
||||
stages:
|
||||
|
|
@ -242,9 +238,7 @@ pipelines:
|
|||
device: disk
|
||||
static-configs: true
|
||||
deployment:
|
||||
ref: ostree/1/1/0
|
||||
osname:
|
||||
mpp-format-string: '{osname}'
|
||||
default: true
|
||||
devices:
|
||||
disk:
|
||||
type: org.osbuild.loopback
|
||||
|
|
@ -408,9 +402,7 @@ pipelines:
|
|||
options:
|
||||
static-configs: true
|
||||
deployment:
|
||||
ref: ostree/1/1/0
|
||||
osname:
|
||||
mpp-format-string: '{osname}'
|
||||
default: true
|
||||
devices:
|
||||
disk:
|
||||
type: org.osbuild.loopback
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import unittest
|
|||
|
||||
import pytest
|
||||
|
||||
from osbuild.testutil import make_fake_tree
|
||||
from osbuild.util import ostree
|
||||
|
||||
from .. import test
|
||||
|
|
@ -208,3 +209,41 @@ class TestPasswdLike(unittest.TestCase):
|
|||
check.read_from(file)
|
||||
|
||||
assert subids.db == check.db
|
||||
|
||||
|
||||
def test_parse_default_deployment_happy(tmp_path):
|
||||
deployment = {
|
||||
"default": True,
|
||||
}
|
||||
make_fake_tree(tmp_path, {
|
||||
"ostree/deploy/fedora-coreos/deploy/72f807.0": "",
|
||||
})
|
||||
osname, ref, serial = ostree.parse_deployment_option(tmp_path, deployment)
|
||||
assert osname == "fedora-coreos"
|
||||
assert ref == "72f807"
|
||||
assert serial == "0"
|
||||
|
||||
|
||||
def test_parse_default_deployment_sad(tmp_path):
|
||||
deployment = {
|
||||
"default": True,
|
||||
}
|
||||
make_fake_tree(tmp_path, {
|
||||
"ostree/deploy/fedora-coreos/deploy/72f807.0": "",
|
||||
"ostree/deploy/fedora-coreos/deploy/123456.0": "",
|
||||
})
|
||||
with pytest.raises(ValueError) as exp:
|
||||
ostree.parse_deployment_option(tmp_path, deployment)
|
||||
assert "More than one deployment found: [" in str(exp.value)
|
||||
|
||||
|
||||
def test_parse_deployment_happy(tmp_path):
|
||||
deployment = {
|
||||
"osname": "fedora-coreos",
|
||||
"ref": "some-ref",
|
||||
"serial": "0",
|
||||
}
|
||||
osname, ref, serial = ostree.parse_deployment_option(tmp_path, deployment)
|
||||
assert osname == "fedora-coreos"
|
||||
assert ref == "some-ref"
|
||||
assert serial == "0"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue