Skip to content

Commit df6b044

Browse files
committed
repository: Make snapshot/targets info required properties
This does not make the examples simpler now, but it will when there are multiple locations where snapshot/timestamp are called. * This way the snapshot/timestamp input material is an internal detail of Repository and the call sites will be simpler. * Both methods now have a "force" argument that can be used to create a new version regardless of meta info changes * but implementations are now required to implement snapshot_info and targets_infos properties that represent the current snapshot and targets versions in the repository Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
1 parent 5d83153 commit df6b044

2 files changed

Lines changed: 68 additions & 30 deletions

File tree

examples/repository/_simplerepo.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ def __init__(self) -> None:
7171
with self.edit(role, init=True):
7272
pass
7373

74+
@property
75+
def targets_infos(self) -> Dict[str, MetaFile]:
76+
# TODO should track changes to snapshot meta and not recreate it here
77+
targets: Targets = self.role_cache["targets"][-1].signed
78+
return {"targets.json": MetaFile(targets.version)}
79+
80+
@property
81+
def snapshot_info(self) -> MetaFile:
82+
snapshot = self.role_cache["snapshot"][-1].signed
83+
return MetaFile(snapshot.version)
84+
7485
def open(self, role: str, init: bool = False) -> Metadata:
7586
"""Return current Metadata for role from 'storage' (or create a new one)"""
7687

@@ -115,7 +126,5 @@ def add_target(self, path: str, content: str) -> None:
115126
logger.debug("Targets v%d", targets.version)
116127

117128
# update snapshot, timestamp
118-
meta = {"targets.json": MetaFile(targets.version)}
119-
new_version, _ = self.snapshot(meta)
120-
if new_version is not None:
121-
self.timestamp(MetaFile(new_version))
129+
self.snapshot()
130+
self.timestamp()

tuf/repository/_repository.py

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,24 @@ def close(self, role: str, md: Metadata, sign_only: bool = False) -> None:
4747
with ones from all available keys."""
4848
raise NotImplementedError
4949

50+
@property
51+
@abstractmethod
52+
def targets_infos(self) -> Dict[str, MetaFile]:
53+
"""Returns the current targets version information
54+
55+
Not that there is a difference between this and the published snapshot
56+
meta: This dictionary reflects the targets metadata that currently
57+
exists in the repository, but the dictionary published by snapshot()
58+
will also include metadata that no longer exists in the repository.
59+
"""
60+
raise NotImplementedError
61+
62+
@property
63+
@abstractmethod
64+
def snapshot_info(self) -> MetaFile:
65+
"""Returns the information matching current snapshot metadata"""
66+
raise NotImplementedError
67+
5068
@contextmanager
5169
def edit(
5270
self, role: str, init: bool = False
@@ -71,69 +89,80 @@ def sign(self, role: str) -> None:
7189
md = self.open(role)
7290
self.close(role, md, sign_only=True)
7391

74-
def snapshot(
75-
self, current_targets: Dict[str, MetaFile]
76-
) -> Tuple[Optional[int], Dict[str, MetaFile]]:
92+
def snapshot(self, force: bool = False) -> Tuple[bool, Dict[str, MetaFile]]:
7793
"""Update snapshot meta information
7894
79-
Updates the meta information in snapshot according to input.
95+
Updates the snapshot meta information according to current targets
96+
metadata state and the current current snapshot meta information.
8097
8198
Arguments:
82-
current_targets: The new currently served targets roles.
99+
force: should new snapshot version be created even if meta
100+
information would not change?
83101
84102
Returns: Tuple of
85-
- New snapshot version or None if snapshot was not created
86-
- Meta information for targets metadata that were removed from repository
103+
- True if snapshot was created, False if not
104+
- Meta information for targets metadata was removed from snapshot
87105
"""
88106

89107
# Snapshot update is needed if
90108
# * any targets files are not yet in snapshot or
91109
# * any targets version is incorrect
92-
updated_snapshot = False
110+
update_version = force
93111
removed: Dict[str, MetaFile] = {}
94112

95113
with self.edit("snapshot") as snapshot:
96-
for keyname, new_meta in current_targets.items():
114+
for keyname, new_meta in self.targets_infos.items():
97115
if keyname not in snapshot.meta:
98-
updated_snapshot = True
116+
update_version = True
99117
snapshot.meta[keyname] = new_meta
100118
continue
101119

102120
old_meta = snapshot.meta[keyname]
103121
if new_meta.version < old_meta.version:
104122
raise ValueError(f"{keyname} version rollback")
105123
if new_meta.version > old_meta.version:
106-
updated_snapshot = True
124+
update_version = True
107125
snapshot.meta[keyname] = new_meta
108126
removed[keyname] = old_meta
109127

110-
if not updated_snapshot:
128+
if not update_version:
111129
# prevent edit() from storing a new snapshot version
112130
raise AbortEdit("Skip snapshot: No targets version changes")
113131

114-
if not updated_snapshot:
115-
# This code is reacheable as edit() handles AbortEdit
116-
logger.debug("Snapshot update not needed") # type: ignore[unreachable]
132+
if not update_version:
133+
logger.debug("Snapshot update not needed")
117134
else:
118135
logger.debug(
119136
"Snapshot v%d, %d targets", snapshot.version, len(snapshot.meta)
120137
)
121138

122-
version = snapshot.version if updated_snapshot else None
123-
return version, removed
139+
return update_version, removed
124140

125-
def timestamp(self, snapshot_meta: MetaFile) -> Optional[MetaFile]:
141+
def timestamp(self, force: bool = False) -> Tuple[bool, Optional[MetaFile]]:
126142
"""Update timestamp meta information
127143
128-
Updates timestamp with given snapshot information.
144+
Updates timestamp according to current snapshot state
129145
130-
Returns the snapshot that was removed from repository (if any).
146+
Returns: Tuple of
147+
- True if timestamp was created, False if not
148+
- Meta information for snapshot metadata that was removed from timestamp
131149
"""
150+
update_version = force
151+
removed = None
132152
with self.edit("timestamp") as timestamp:
133-
old_snapshot_meta = timestamp.snapshot_meta
134-
timestamp.snapshot_meta = snapshot_meta
153+
if self.snapshot_info.version < timestamp.snapshot_meta.version:
154+
raise ValueError(f"snapshot version rollback")
135155

136-
logger.debug("Timestamp v%d", timestamp.version)
137-
if old_snapshot_meta.version == snapshot_meta.version:
138-
return None
139-
return old_snapshot_meta
156+
if self.snapshot_info.version > timestamp.snapshot_meta.version:
157+
update_version = True
158+
removed = timestamp.snapshot_meta
159+
timestamp.snapshot_meta = self.snapshot_info
160+
161+
if not update_version:
162+
raise AbortEdit("Skip timestamp: No snapshot version changes")
163+
164+
if not update_version:
165+
logger.debug("Timestamp update not needed")
166+
else:
167+
logger.debug("Timestamp v%d", timestamp.version)
168+
return update_version, removed

0 commit comments

Comments
 (0)