Skip to content

Commit de2644f

Browse files
committed
Breaking: new API: consistent_snapshot optional
NOTE: making consistent_snapshot optional requires using a default value for the argument in __init__ in Root and thus consistent_snapshot should be rearranged in the end. Read more: #1394 (comment) From chapter 7 in the spec (version 1.0.17) "Finally, the root metadata should write the Boolean "consistent_snapshot" attribute at the root level of its keys of attributes. If consistent snapshots are not written by the repository, then the attribute may either be left unspecified or be set to the False value. Otherwise, it must be set to the True value." We want to make sure we support repositories without consistent_snapshot set. Signed-off-by: Martin Vrachev <[email protected]>
1 parent 9a397b9 commit de2644f

File tree

2 files changed

+13
-6
lines changed

2 files changed

+13
-6
lines changed

tests/test_api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,12 @@ def test_metadata_root(self):
425425
with self.assertRaises(KeyError):
426426
root.signed.remove_key('root', 'nosuchkey')
427427

428+
# Test serializing and deserializing without consistent_snapshot.
429+
root_dict = root.to_dict()
430+
del root_dict["signed"]["consistent_snapshot"]
431+
root = Root.from_dict(copy.deepcopy(root_dict["signed"]))
432+
self.assertEqual(root_dict["signed"], root.to_dict())
433+
428434
def test_delegated_role_class(self):
429435
roles = [
430436
{

tuf/api/metadata.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -503,8 +503,8 @@ class Root(Signed):
503503
"""A container for the signed part of root metadata.
504504
505505
Attributes:
506-
consistent_snapshot: A boolean indicating whether the repository
507-
supports consistent snapshots.
506+
consistent_snapshot: An optional boolean indicating whether the
507+
repository supports consistent snapshots.
508508
keys: A dictionary that contains a public key store used to verify
509509
top level roles metadata signatures::
510510
@@ -534,9 +534,9 @@ def __init__(
534534
version: int,
535535
spec_version: str,
536536
expires: datetime,
537-
consistent_snapshot: bool,
538537
keys: Dict[str, Key],
539538
roles: Dict[str, Role],
539+
consistent_snapshot: Optional[bool] = None,
540540
unrecognized_fields: Optional[Mapping[str, Any]] = None,
541541
) -> None:
542542
super().__init__(version, spec_version, expires, unrecognized_fields)
@@ -548,7 +548,7 @@ def __init__(
548548
def from_dict(cls, root_dict: Dict[str, Any]) -> "Root":
549549
"""Creates Root object from its dict representation."""
550550
common_args = cls._common_fields_from_dict(root_dict)
551-
consistent_snapshot = root_dict.pop("consistent_snapshot")
551+
consistent_snapshot = root_dict.pop("consistent_snapshot", None)
552552
keys = root_dict.pop("keys")
553553
roles = root_dict.pop("roles")
554554

@@ -558,7 +558,7 @@ def from_dict(cls, root_dict: Dict[str, Any]) -> "Root":
558558
roles[role_name] = Role.from_dict(role_dict)
559559

560560
# All fields left in the root_dict are unrecognized.
561-
return cls(*common_args, consistent_snapshot, keys, roles, root_dict)
561+
return cls(*common_args, keys, roles, consistent_snapshot, root_dict)
562562

563563
def to_dict(self) -> Dict[str, Any]:
564564
"""Returns the dict representation of self."""
@@ -567,10 +567,11 @@ def to_dict(self) -> Dict[str, Any]:
567567
roles = {}
568568
for role_name, role in self.roles.items():
569569
roles[role_name] = role.to_dict()
570+
if self.consistent_snapshot is not None:
571+
root_dict["consistent_snapshot"] = self.consistent_snapshot
570572

571573
root_dict.update(
572574
{
573-
"consistent_snapshot": self.consistent_snapshot,
574575
"keys": keys,
575576
"roles": roles,
576577
}

0 commit comments

Comments
 (0)