osbuild: no auto commit of the last stage

Do not automatically commit the last stage of the pipeline to the
store. The last stage is most likely not what should be cached,
because it will contain all the individual customization and thus
be very likely different for different users. Instead, the dnf or
rpm stages have a higher chance of being the same and thus are
better candidates for caching.
Technically this change is done via two big changes that build
upon new features introduces in the previous commits, most notably
the copy on write semantics of Object and that input/output is
being done via `objectstore.Object` instead of plain paths. The
first of the two big changes is  to create one new `Object` at
the beginning of `pipeline.run` and use that, in write mode via
`Object.write` across invocations of `stage.run` calls, with
checkpoints being created after each stage on demand.
The very same `Object` is then used in read mode via `Object.read`
as the input tree for the Assembler. After the assembler is done
the resulting image/tree is manually committed to the store.
The other big change is to remove the `ObjectStore.commit` call
from the `ObjectStore.new` method and thus the automatic commit
after the last stage is gone.
NB: since the build tree is being retrieved in `get_buildtree`
from the store, a checkpoint for the last stage of the build
pipeline is forced for now. Future commits will refactor will
do away with that forced commit as well.
Change osbuildtest.TestCase to always create a checkpoint at
the final tree (the last stage of the pipeline), since tests
need it to check the tree contents.
This commit is contained in:
Christian Kellner 2020-02-18 18:19:04 +01:00 committed by Tom Gundersen
parent 7720b5508d
commit 42a365d12f
4 changed files with 66 additions and 45 deletions

View file

@ -216,32 +216,28 @@ class ObjectStore:
yield path
@contextlib.contextmanager
def new(self, object_id, base_id=None):
"""Creates a new `Object` for `object_id`.
def new(self, base_id=None):
"""Creates a new temporary `Object`.
This method must be used as a context manager. It returns a new
temporary instance of `Object`. It will only be committed to the
store if the context completes without raising an exception.
This method must be used as a context manager. It returns
a temporary instance of `Object`, which can then be used
for interaction with the store.
If changes to the object's content were made (by calling
`Object.write`), these must manually be committed to the
store via `commit()`.
"""
with Object(self) as obj:
# the object that is yielded will be added to the content store
# on success as object_id
if base_id:
# if we were given a base id then this is the base for the
# new object
# NB: its initialization is deferred to the first write
# if we were given a base id then this is the base
# content for the new object
# NB: `Object` has copy-on-write semantics, so no
# copying of the data takes places at this point
obj.base = base_id
yield obj
# if the yield above raises an exception, the working tree
# is cleaned up by tempfile, otherwise, the it the content
# of it was created or modified by the caller. All that is
# left to do is to commit it to the object store
self.commit(obj, object_id)
def commit(self, obj: Object, object_id: str) -> str:
"""Commits a Object to the object store