stage: add module information about itself

Add a new `info` property that holds the `meta.ModuleInfo` info
for the stage. This gives each instance of a stage access to
meta (or class) information about it, i.e. its schema, docs but,
more importantly, also its name and path to the executable.
Thefore the `name` property is coverted into a transient property
which access the `name` member of `info`.
Change the `formats/v1` load mechanism to carry a new `index`
argument which is used to load the `ModuleInfo` for each stage.
Adapt all tests to load the info as well when creating stages.
This commit is contained in:
Christian Kellner 2021-01-05 19:43:11 +01:00 committed by Tom Gundersen
parent a26b7291d1
commit 7a6c2df910
6 changed files with 41 additions and 20 deletions

View file

@ -43,20 +43,20 @@ def describe(manifest: Manifest, *, with_id=False) -> Dict:
return description return description
def load_build(description: Dict, result: List[Pipeline]): def load_build(description: Dict, index: Index, result: List[Pipeline]):
pipeline = description.get("pipeline") pipeline = description.get("pipeline")
if pipeline: if pipeline:
build_pipeline = load_pipeline(pipeline, result) build_pipeline = load_pipeline(pipeline, index, result)
else: else:
build_pipeline = None build_pipeline = None
return build_pipeline, description["runner"] return build_pipeline, description["runner"]
def load_pipeline(description: Dict, result: List[Pipeline]) -> Pipeline: def load_pipeline(description: Dict, index: Index, result: List[Pipeline]) -> Pipeline:
build = description.get("build") build = description.get("build")
if build: if build:
build_pipeline, runner = load_build(build, result) build_pipeline, runner = load_build(build, index, result)
else: else:
build_pipeline, runner = None, detect_host_runner() build_pipeline, runner = None, detect_host_runner()
@ -64,7 +64,8 @@ def load_pipeline(description: Dict, result: List[Pipeline]) -> Pipeline:
pipeline = Pipeline(runner, build_id) pipeline = Pipeline(runner, build_id)
for s in description.get("stages", []): for s in description.get("stages", []):
pipeline.add_stage(s["name"], s.get("options", {})) info = index.get_module_info("Stage", s["name"])
pipeline.add_stage(info, s.get("options", {}))
a = description.get("assembler") a = description.get("assembler")
if a: if a:
@ -75,7 +76,7 @@ def load_pipeline(description: Dict, result: List[Pipeline]) -> Pipeline:
return pipeline return pipeline
def load(description: Dict) -> Manifest: def load(description: Dict, index: Index) -> Manifest:
"""Load a manifest description""" """Load a manifest description"""
pipeline = description.get("pipeline", {}) pipeline = description.get("pipeline", {})
@ -83,7 +84,7 @@ def load(description: Dict) -> Manifest:
pipelines = [] pipelines = []
load_pipeline(pipeline, pipelines) load_pipeline(pipeline, index, pipelines)
for pipeline in pipelines: for pipeline in pipelines:
for stage in pipeline.stages: for stage in pipeline.stages:

View file

@ -90,7 +90,7 @@ def osbuild_cli():
show_validation(res, args.manifest_path) show_validation(res, args.manifest_path)
return 2 return 2
manifest = fmt.load(desc) manifest = fmt.load(desc, index)
if args.checkpoint: if args.checkpoint:
missed = manifest.mark_checkpoints(args.checkpoint) missed = manifest.mark_checkpoints(args.checkpoint)

View file

@ -33,14 +33,18 @@ class BuildResult:
class Stage: class Stage:
def __init__(self, name, source_options, build, base, options): def __init__(self, info, source_options, build, base, options):
self.name = name self.info = info
self.sources = source_options self.sources = source_options
self.build = build self.build = build
self.base = base self.base = base
self.options = options self.options = options
self.checkpoint = False self.checkpoint = False
@property
def name(self):
return self.info.name
@property @property
def id(self): def id(self):
m = hashlib.sha256() m = hashlib.sha256()
@ -159,8 +163,8 @@ class Pipeline:
def output_id(self): def output_id(self):
return self.assembler.id if self.assembler else None return self.assembler.id if self.assembler else None
def add_stage(self, name, options, sources_options=None): def add_stage(self, info, options, sources_options=None):
stage = Stage(name, sources_options, self.build, self.tree_id, options or {}) stage = Stage(info, sources_options, self.build, self.tree_id, options or {})
self.stages.append(stage) self.stages.append(stage)
if self.assembler: if self.assembler:
self.assembler.base = stage.id self.assembler.base = stage.id

View file

@ -55,9 +55,12 @@ class TestMonitor(unittest.TestCase):
@unittest.skipUnless(test.TestBase.can_bind_mount(), "root-only") @unittest.skipUnless(test.TestBase.can_bind_mount(), "root-only")
def test_log_monitor_vfuncs(self): def test_log_monitor_vfuncs(self):
# Checks the basic functioning of the LogMonitor # Checks the basic functioning of the LogMonitor
index = osbuild.meta.Index(os.curdir)
runner = detect_host_runner() runner = detect_host_runner()
pipeline = osbuild.Pipeline(runner=runner) pipeline = osbuild.Pipeline(runner=runner)
pipeline.add_stage("org.osbuild.noop", { info = index.get_module_info("Stage", "org.osbuild.noop")
pipeline.add_stage(info, {
"isthisthereallife": False "isthisthereallife": False
}) })
pipeline.set_assembler("org.osbuild.noop") pipeline.set_assembler("org.osbuild.noop")
@ -87,11 +90,14 @@ class TestMonitor(unittest.TestCase):
def test_monitor_integration(self): def test_monitor_integration(self):
# Checks the monitoring API is called properly from the pipeline # Checks the monitoring API is called properly from the pipeline
runner = detect_host_runner() runner = detect_host_runner()
index = osbuild.meta.Index(os.curdir)
pipeline = osbuild.Pipeline(runner=runner) pipeline = osbuild.Pipeline(runner=runner)
pipeline.add_stage("org.osbuild.noop", { noop_info = index.get_module_info("Stage", "org.osbuild.noop")
pipeline.add_stage(noop_info, {
"isthisthereallife": False "isthisthereallife": False
}) })
pipeline.add_stage("org.osbuild.noop", { pipeline.add_stage(noop_info, {
"isthisjustfantasy": True "isthisjustfantasy": True
}) })
pipeline.set_assembler("org.osbuild.noop") pipeline.set_assembler("org.osbuild.noop")

View file

@ -22,6 +22,8 @@ class TestDescriptions(unittest.TestCase):
"""Degenerate case. Make sure we always return the same canonical """Degenerate case. Make sure we always return the same canonical
description when passing empty or null values.""" description when passing empty or null values."""
index = osbuild.meta.Index(os.curdir)
cases = [ cases = [
{}, {},
{"assembler": None}, {"assembler": None},
@ -32,12 +34,14 @@ class TestDescriptions(unittest.TestCase):
for pipeline in cases: for pipeline in cases:
manifest = {"pipeline": pipeline} manifest = {"pipeline": pipeline}
with self.subTest(pipeline): with self.subTest(pipeline):
desc = fmt.describe(fmt.load(manifest)) desc = fmt.describe(fmt.load(manifest, index))
self.assertEqual(desc["pipeline"], {}) self.assertEqual(desc["pipeline"], {})
@unittest.skipUnless(test.TestBase.can_bind_mount(), "root-only") @unittest.skipUnless(test.TestBase.can_bind_mount(), "root-only")
def test_stage_run(self): def test_stage_run(self):
stage = osbuild.Stage("org.osbuild.noop", {}, None, None, {}) index = osbuild.meta.Index(os.curdir)
info = index.get_module_info("Stage", "org.osbuild.noop")
stage = osbuild.Stage(info, {}, None, None, {})
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
@ -79,11 +83,14 @@ class TestDescriptions(unittest.TestCase):
self.assertEqual(res.id, asm.id) self.assertEqual(res.id, asm.id)
def test_pipeline(self): def test_pipeline(self):
index = osbuild.meta.Index(os.curdir)
test_info = index.get_module_info("Stage", "org.osbuild.test")
build = osbuild.Pipeline("org.osbuild.test") build = osbuild.Pipeline("org.osbuild.test")
build.add_stage("org.osbuild.test", {"one": 1}) build.add_stage(test_info, {"one": 1})
pipeline = osbuild.Pipeline("org.osbuild.test", build.tree_id) pipeline = osbuild.Pipeline("org.osbuild.test", build.tree_id)
pipeline.add_stage("org.osbuild.test", {"one": 2}) pipeline.add_stage(test_info, {"one": 2})
pipeline.set_assembler("org.osbuild.test") pipeline.set_assembler("org.osbuild.test")
manifest = osbuild.Manifest([build, pipeline], {}) manifest = osbuild.Manifest([build, pipeline], {})

View file

@ -11,6 +11,7 @@ import sys
import tempfile import tempfile
import unittest import unittest
import osbuild.meta
from osbuild.formats import v1 as fmt from osbuild.formats import v1 as fmt
from osbuild.util import linux from osbuild.util import linux
@ -352,9 +353,11 @@ class OSBuild(contextlib.AbstractContextManager):
are defined. are defined.
""" """
index = osbuild.meta.Index(os.curdir)
manifest_json = json.loads(manifest_data) manifest_json = json.loads(manifest_data)
manifest = fmt.load(manifest_json) manifest = fmt.load(manifest_json, index)
tree_id, _ = fmt.get_ids(manifest) tree_id, _ = fmt.get_ids(manifest)
return tree_id return tree_id