Skip to content

Commit c79ea55

Browse files
committed
Improve delegated roles loading in load_repository()
Replace the list used for the delegations graph traversal with a deque and use a set to store already loaded roles and avoid loops in case of cycles in the graph. Improve comments and readability. Signed-off-by: Teodora Sechkova <[email protected]>
1 parent da09a22 commit c79ea55

File tree

1 file changed

+33
-18
lines changed

1 file changed

+33
-18
lines changed

tuf/repository_tool.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import shutil
4040
import json
4141

42+
from collections import deque
43+
4244
import tuf
4345
import tuf.formats
4446
import tuf.roledb
@@ -3100,29 +3102,44 @@ def load_repository(repository_directory, repository_name='default',
31003102
repository, consistent_snapshot = repo_lib._load_top_level_metadata(repository,
31013103
filenames, repository_name)
31023104

3103-
# Load the delegated targets metadata and generate their fileinfo. The
3104-
# extracted fileinfo is stored in the 'meta' field of the snapshot metadata
3105-
# object.
3105+
delegated_roles_filenames = repo_lib.get_delegations_filenames(
3106+
metadata_directory, consistent_snapshot, storage_backend)
3107+
3108+
# Load the delegated targets metadata and their fileinfo.
3109+
# The delegated targets roles form a tree/graph which is traversed in a
3110+
# breadth-first-search manner starting from 'targets' in order to correctly
3111+
# load the delegations hierarchy.
31063112
targets_objects = {}
31073113
targets_objects['targets'] = repository.targets
3108-
# A list of delegated-delegating role pairs
3109-
delegations = []
31103114

3111-
delegations_filenames = repo_lib.get_delegations_filenames(metadata_directory,
3112-
consistent_snapshot, storage_backend)
3115+
# A deque used to keep the next delegation to be loaded
3116+
delegations = deque()
3117+
# A set used to keep the already loaded delegations and avoid an infinite
3118+
# loop in case of cycles in the graph
3119+
loaded_delegations = set()
31133120

31143121
# Top-level roles are already loaded, fetch targets and get its delegations.
3115-
# Collect a list of delegated-delegating role pairs, starting from the
3116-
# top-level targets: [('role1', 'targets'), ('role2', 'targets'), ... ]
3122+
# Store the delegations in the form of delegated-delegating role tuples,
3123+
# starting from the top-level targets:
3124+
# [('role1', 'targets'), ('role2', 'targets'), ... ]
31173125
roleinfo = tuf.roledb.get_roleinfo('targets', repository_name)
31183126
for role in roleinfo['delegations']['roles']:
3119-
delegations.append([role['name'], 'targets'])
3127+
delegations.append((role['name'], 'targets'))
31203128

3121-
# Load the delegated roles by starting from 'targets' and continuously
3122-
# appending the next level delegations to the list
3123-
for rolename, delegating_role in delegations:
3124-
metadata_path = delegations_filenames[rolename]
3129+
# Traverse the graph by appending the next delegation to the deque and
3130+
# 'pop'-ing and loading the left-most element.
3131+
while delegations:
3132+
rolename, delegating_role = delegations.popleft()
3133+
if (rolename, delegating_role) in loaded_delegations:
3134+
continue
31253135

3136+
# Instead of adding only rolename to the set, store the already loaded
3137+
# delegated-delegating role tuples. This way a delegated role is added
3138+
# to each of its delegating roles but when the role is reached twice
3139+
# from the same delegating role an infinite loop is avoided.
3140+
loaded_delegations.add((rolename, delegating_role))
3141+
3142+
metadata_path = delegated_roles_filenames[rolename]
31263143
signable = None
31273144

31283145
try:
@@ -3149,8 +3166,6 @@ def load_repository(repository_directory, repository_name='default',
31493166
roleinfo['paths'] = metadata_object['targets']
31503167
roleinfo['delegations'] = metadata_object['delegations']
31513168

3152-
tuf.roledb.add_role(rolename, roleinfo, repository_name)
3153-
31543169
# Generate the Targets object of the delegated role,
31553170
# add it to the top-level 'targets' object and to its
31563171
# direct delegating role object.
@@ -3165,10 +3180,10 @@ def load_repository(repository_directory, repository_name='default',
31653180
targets_objects[delegating_role].add_delegated_role(rolename,
31663181
new_targets_object)
31673182

3168-
# Append the next level delegations to the list:
3183+
# Append the next level delegations to the deque:
31693184
# the 'delegated' role becomes the 'delegating'
31703185
for delegation in metadata_object['delegations']['roles']:
3171-
delegations.append([delegation['name'], rolename])
3186+
delegations.append((delegation['name'], rolename))
31723187

31733188
# Extract the keys specified in the delegations field of the Targets
31743189
# role. Add 'key_object' to the list of recognized keys. Keys may be

0 commit comments

Comments
 (0)