pipeline: proper object cleanup on errors

The recent changes removed the {Assembler,Stage}Failed exceptions,
which includes them being thrown from Stage.run and Assembler.run.
Instead result dictionaries are returned even on errors. But the
object store, used as a context manager, relies on exceptions to
detect the error case and thus needs them to cleanup the temporary
objects. Without those exceptions the temporary objects end up in
the store even when the sage or assembler failed.
Restore the old behavior by throwing a generic BuildError exception
from the Stage and Assembler, which will be caught directly in the
pipeline and converted to a result dict.
This commit is contained in:
Christian Kellner 2019-12-17 12:22:38 +01:00 committed by Lars Karlitski
parent 9350e868a7
commit ede3f6baeb

View file

@ -17,6 +17,27 @@ RESET = "\033[0m"
BOLD = "\033[1m"
class BuildResult:
def __init__(self, origin, returncode, output):
self.name = origin.name
self.id = origin.id
self.options = origin.options
self.success = returncode == 0
self.output = output
def as_dict(self):
return vars(self)
class BuildError(Exception):
def __init__(self, result):
super(BuildError, self).__init__()
self.result = result
def as_dict(self):
return self.result.as_dict()
def print_header(title, options):
print()
print(f"{RESET}{BOLD}{title}{RESET} " + json.dumps(options or {}, indent=2))
@ -63,15 +84,10 @@ class Stage:
binds=[f"{tree}:/run/osbuild/tree"],
stdin=subprocess.DEVNULL,
)
return {
"name": self.name,
"id": self.id,
"options": self.options,
"success": r.returncode == 0,
"output": api.output
}
return r.returncode == 0
res = BuildResult(self, r.returncode, api.output)
if not res.success:
raise BuildError(res)
return res
class Assembler:
@ -130,13 +146,10 @@ class Assembler:
readonly_binds=ro_binds,
stdin=subprocess.DEVNULL,
)
return {
"name": self.name,
"id": self.id,
"options": self.options,
"success": r.returncode == 0,
"output": api.output
}
res = BuildResult(self, r.returncode, api.output)
if not res.success:
raise BuildError(res)
return res
class Pipeline:
@ -229,34 +242,38 @@ class Pipeline:
# in the content store. However, we guarantee that all tree_id's and all
# generated trees remain valid.
results["stages"] = []
with object_store.new(self.tree_id, base_id=base) as tree:
for stage in self.stages[base_idx + 1:]:
r = stage.run(tree,
self.runner,
build_tree,
interactive=interactive,
libdir=libdir)
results["stages"].append(r)
if not r["success"]:
results["success"] = False
return results
try:
with object_store.new(self.tree_id, base_id=base) as tree:
for stage in self.stages[base_idx + 1:]:
r = stage.run(tree,
self.runner,
build_tree,
interactive=interactive,
libdir=libdir)
results["stages"].append(r.as_dict())
except BuildError as err:
results["stages"].append(err.as_dict())
results["success"] = False
return results
results["tree_id"] = self.tree_id
if self.assembler:
if not object_store.contains(self.output_id):
with object_store.get(self.tree_id) as tree, \
object_store.new(self.output_id) as output_dir:
r = self.assembler.run(tree,
self.runner,
build_tree,
output_dir=output_dir,
interactive=interactive,
libdir=libdir)
results["assembler"] = r
if not r["success"]:
results["success"] = False
return results
try:
with object_store.get(self.tree_id) as tree, \
object_store.new(self.output_id) as output_dir:
r = self.assembler.run(tree,
self.runner,
build_tree,
output_dir=output_dir,
interactive=interactive,
libdir=libdir)
results["assembler"] = r.as_dict()
except BuildError as err:
results["assembler"] = err.as_dict()
results["success"] = False
return results
results["output_id"] = self.output_id