|
@@ -75,9 +75,71 @@ class Archives(abc.MutableMapping):
|
|
|
str timestamps or datetime timestamps.
|
|
|
"""
|
|
|
|
|
|
- def __init__(self):
|
|
|
+ def __init__(self, repository):
|
|
|
+ from .repository import Repository
|
|
|
+ from .remote import RemoteRepository
|
|
|
+
|
|
|
+ self.repository = repository
|
|
|
+ self.legacy = not isinstance(repository, (Repository, RemoteRepository))
|
|
|
# key: str archive name, value: dict('id': bytes_id, 'time': str_iso_ts)
|
|
|
self._archives = {}
|
|
|
+ self.manifest = None
|
|
|
+
|
|
|
+ def prepare(self, manifest, m):
|
|
|
+ self.manifest = manifest
|
|
|
+ if not self.legacy:
|
|
|
+ self.load()
|
|
|
+ else:
|
|
|
+ self.set_raw_dict(m.archives)
|
|
|
+
|
|
|
+ def finish(self, manifest):
|
|
|
+ self.manifest = manifest # note: .prepare is not always called
|
|
|
+ if not self.legacy:
|
|
|
+ self.save()
|
|
|
+ manifest_archives = {}
|
|
|
+ else:
|
|
|
+ manifest_archives = StableDict(self.get_raw_dict())
|
|
|
+ return manifest_archives
|
|
|
+
|
|
|
+ def load(self):
|
|
|
+ # load archives list from store
|
|
|
+ from .helpers import msgpack
|
|
|
+
|
|
|
+ archives = {}
|
|
|
+ try:
|
|
|
+ infos = list(self.repository.store_list("archives"))
|
|
|
+ except ObjectNotFound:
|
|
|
+ infos = []
|
|
|
+ for info in infos:
|
|
|
+ info = ItemInfo(*info) # RPC does not give us a NamedTuple
|
|
|
+ value = self.repository.store_load(f"archives/{info.name}")
|
|
|
+ _, value = self.manifest.repo_objs.parse(hex_to_bin(info.name), value, ro_type=ROBJ_MANIFEST)
|
|
|
+ archive = msgpack.unpackb(value)
|
|
|
+ archives[archive["name"]] = dict(id=archive["id"], time=archive["time"])
|
|
|
+ self.set_raw_dict(archives)
|
|
|
+
|
|
|
+ def save(self):
|
|
|
+ # save archives list to store
|
|
|
+ valid_keys = set()
|
|
|
+ for name, info in self.get_raw_dict().items():
|
|
|
+ archive = dict(name=name, id=info["id"], time=info["time"])
|
|
|
+ value = self.manifest.key.pack_metadata(archive) #
|
|
|
+ id = self.manifest.repo_objs.id_hash(value)
|
|
|
+ key = bin_to_hex(id)
|
|
|
+ value = self.manifest.repo_objs.format(id, {}, value, ro_type=ROBJ_MANIFEST)
|
|
|
+ self.repository.store_store(f"archives/{key}", value)
|
|
|
+ valid_keys.add(key)
|
|
|
+ # now, delete all other keys in archives/ which are not in valid keys / in the manifest anymore.
|
|
|
+ # TODO: this is a dirty hack to simulate the old manifest behaviour closely, but also means
|
|
|
+ # keeping its problems, like read-modify-write behaviour requiring an exclusive lock.
|
|
|
+ try:
|
|
|
+ infos = list(self.repository.store_list("archives"))
|
|
|
+ except ObjectNotFound:
|
|
|
+ infos = []
|
|
|
+ for info in infos:
|
|
|
+ info = ItemInfo(*info) # RPC does not give us a NamedTuple
|
|
|
+ if info.name not in valid_keys:
|
|
|
+ self.repository.store_delete(f"archives/{info.name}")
|
|
|
|
|
|
def __len__(self):
|
|
|
return len(self._archives)
|
|
@@ -222,7 +284,7 @@ class Manifest:
|
|
|
MANIFEST_ID = b"\0" * 32
|
|
|
|
|
|
def __init__(self, key, repository, item_keys=None, ro_cls=RepoObj):
|
|
|
- self.archives = Archives()
|
|
|
+ self.archives = Archives(repository)
|
|
|
self.config = {}
|
|
|
self.key = key
|
|
|
self.repo_objs = ro_cls(key)
|
|
@@ -242,8 +304,6 @@ class Manifest:
|
|
|
def load(cls, repository, operations, key=None, *, ro_cls=RepoObj):
|
|
|
from .item import ManifestItem
|
|
|
from .crypto.key import key_factory
|
|
|
- from .remote import RemoteRepository
|
|
|
- from .repository import Repository
|
|
|
|
|
|
cdata = repository.get_manifest()
|
|
|
if not key:
|
|
@@ -255,25 +315,7 @@ class Manifest:
|
|
|
manifest.id = manifest.repo_objs.id_hash(data)
|
|
|
if m.get("version") not in (1, 2):
|
|
|
raise ValueError("Invalid manifest version")
|
|
|
-
|
|
|
- if isinstance(repository, (Repository, RemoteRepository)):
|
|
|
- from .helpers import msgpack
|
|
|
-
|
|
|
- archives = {}
|
|
|
- try:
|
|
|
- infos = list(repository.store_list("archives"))
|
|
|
- except ObjectNotFound:
|
|
|
- infos = []
|
|
|
- for info in infos:
|
|
|
- info = ItemInfo(*info) # RPC does not give us a NamedTuple
|
|
|
- value = repository.store_load(f"archives/{info.name}")
|
|
|
- _, value = manifest.repo_objs.parse(hex_to_bin(info.name), value, ro_type=ROBJ_MANIFEST)
|
|
|
- archive = msgpack.unpackb(value)
|
|
|
- archives[archive["name"]] = dict(id=archive["id"], time=archive["time"])
|
|
|
- manifest.archives.set_raw_dict(archives)
|
|
|
- else:
|
|
|
- manifest.archives.set_raw_dict(m.archives)
|
|
|
-
|
|
|
+ manifest.archives.prepare(manifest, m)
|
|
|
manifest.timestamp = m.get("timestamp")
|
|
|
manifest.config = m.config
|
|
|
# valid item keys are whatever is known in the repo or every key we know
|
|
@@ -310,8 +352,6 @@ class Manifest:
|
|
|
|
|
|
def write(self):
|
|
|
from .item import ManifestItem
|
|
|
- from .remote import RemoteRepository
|
|
|
- from .repository import Repository
|
|
|
|
|
|
# self.timestamp needs to be strictly monotonically increasing. Clocks often are not set correctly
|
|
|
if self.timestamp is None:
|
|
@@ -326,32 +366,7 @@ class Manifest:
|
|
|
assert all(len(name) <= 255 for name in self.archives)
|
|
|
assert len(self.item_keys) <= 100
|
|
|
self.config["item_keys"] = tuple(sorted(self.item_keys))
|
|
|
-
|
|
|
- if isinstance(self.repository, (Repository, RemoteRepository)):
|
|
|
- valid_keys = set()
|
|
|
- for name, info in self.archives.get_raw_dict().items():
|
|
|
- archive = dict(name=name, id=info["id"], time=info["time"])
|
|
|
- value = self.key.pack_metadata(archive)
|
|
|
- id = self.repo_objs.id_hash(value)
|
|
|
- key = bin_to_hex(id)
|
|
|
- value = self.repo_objs.format(id, {}, value, ro_type=ROBJ_MANIFEST)
|
|
|
- self.repository.store_store(f"archives/{key}", value)
|
|
|
- valid_keys.add(key)
|
|
|
- # now, delete all other keys in archives/ which are not in valid keys / in the manifest anymore.
|
|
|
- # TODO: this is a dirty hack to simulate the old manifest behaviour closely, but also means
|
|
|
- # keeping its problems, like read-modify-write behaviour requiring an exclusive lock.
|
|
|
- try:
|
|
|
- infos = list(self.repository.store_list("archives"))
|
|
|
- except ObjectNotFound:
|
|
|
- infos = []
|
|
|
- for info in infos:
|
|
|
- info = ItemInfo(*info) # RPC does not give us a NamedTuple
|
|
|
- if info.name not in valid_keys:
|
|
|
- self.repository.store_delete(f"archives/{info.name}")
|
|
|
- manifest_archives = {}
|
|
|
- else:
|
|
|
- manifest_archives = StableDict(self.archives.get_raw_dict())
|
|
|
-
|
|
|
+ manifest_archives = self.archives.finish(self)
|
|
|
manifest = ManifestItem(
|
|
|
version=2, archives=manifest_archives, timestamp=self.timestamp, config=StableDict(self.config)
|
|
|
)
|