Skip to content

Commit 0ce016b

Browse files
Merge pull request #389 from vladimir-v-diaz/develop
Fix for repository.status()
2 parents 42dcd19 + ab35f07 commit 0ce016b

File tree

6 files changed

+189
-7
lines changed

6 files changed

+189
-7
lines changed

tests/test_repository_lib.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,15 @@ def test_write_metadata_file(self):
793793
version_number,
794794
compression_algorithms,
795795
consistent_snapshot=True)
796+
797+
# Try to create a link to root.json when root.json doesn't exist locally.
798+
# repository_lib should log a message if this is the case.
799+
tuf.conf.CONSISTENT_METHOD = 'hard_link'
800+
os.remove(output_filename)
801+
repo_lib.write_metadata_file(root_signable, output_filename,
802+
version_number,
803+
compression_algorithms,
804+
consistent_snapshot=True)
796805

797806
# Reset CONSISTENT_METHOD so that subsequent tests work as expected.
798807
tuf.conf.CONSISTENT_METHOD = 'copy'

tests/test_repository_tool.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,19 @@ def test_writeall(self):
347347
# successfully.
348348
repo_tool.load_repository(repository_directory)
349349

350+
# Verify the behavior of marking and unmarking roles as dirty.
351+
# We begin by ensuring that writeall() cleared the list of dirty roles..
352+
self.assertEqual([], tuf.roledb.get_dirty_roles())
353+
354+
repository.mark_dirty(['root', 'timestamp'])
355+
self.assertEqual(['root', 'timestamp'], sorted(tuf.roledb.get_dirty_roles()))
356+
repository.unmark_dirty(['root'])
357+
self.assertEqual(['timestamp'], tuf.roledb.get_dirty_roles())
358+
359+
# Ensure status() does not leave behind any dirty roles.
360+
repository.status()
361+
self.assertEqual(['timestamp'], tuf.roledb.get_dirty_roles())
362+
350363
# Test improperly formatted arguments.
351364
self.assertRaises(tuf.FormatError, repository.writeall, 3, False)
352365
self.assertRaises(tuf.FormatError, repository.writeall, False, 3)

tests/test_roledb.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,36 @@ def test_mark_dirty(self):
701701
self.assertRaises(tuf.InvalidNameError, tuf.roledb.mark_dirty,
702702
['dirty_role'], 'non-existent')
703703

704+
705+
706+
def test_unmark_dirty(self):
707+
# Add a dirty role to roledb.
708+
rolename = 'targets'
709+
roleinfo1 = {'keyids': ['123'], 'threshold': 1}
710+
tuf.roledb.add_role(rolename, roleinfo1)
711+
rolename2 = 'dirty_role'
712+
roleinfo2 = {'keyids': ['123'], 'threshold': 2}
713+
tuf.roledb.add_role(rolename2, roleinfo2)
714+
mark_role_as_dirty = True
715+
tuf.roledb.update_roleinfo(rolename, roleinfo1, mark_role_as_dirty)
716+
# Note: The 'default' repository is searched if the repository name is
717+
# not given to get_dirty_roles().
718+
self.assertEqual([rolename], tuf.roledb.get_dirty_roles())
719+
tuf.roledb.update_roleinfo(rolename2, roleinfo2, mark_role_as_dirty)
720+
721+
tuf.roledb.unmark_dirty(['dirty_role'])
722+
self.assertEqual([rolename], sorted(tuf.roledb.get_dirty_roles()))
723+
tuf.roledb.unmark_dirty(['targets'])
724+
self.assertEqual([], sorted(tuf.roledb.get_dirty_roles()))
725+
726+
# What happens for a role that isn't dirty? unmark_dirty() should just
727+
# log a message.
728+
tuf.roledb.unmark_dirty(['unknown_role'])
729+
730+
# Verify that a role cannot be unmarked as dirty for a non-existent
731+
# repository.
732+
self.assertRaises(tuf.InvalidNameError, tuf.roledb.unmark_dirty,
733+
['dirty_role'], 'non-existent')
704734

705735

706736
def _test_rolename(self, test_function):

tuf/repository_lib.py

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,7 +1917,7 @@ def sign_metadata(metadata_object, keyids, filename):
19171917
logger.warning('Unable to create signature for keyid: ' + repr(keyid))
19181918

19191919
else:
1920-
logger.warning('Private key unset. Skipping: ' + repr(keyid))
1920+
logger.debug('Private key unset. Skipping: ' + repr(keyid))
19211921

19221922
else:
19231923
raise tuf.Error('The keydb contains a key with an invalid key type.')
@@ -2039,15 +2039,18 @@ def write_metadata_file(metadata, filename, version_number,
20392039
# to its expected filename (e.g., root.json). The option of either
20402040
# creating a copy or link should be configurable in tuf.conf.py.
20412041
if (tuf.conf.CONSISTENT_METHOD == 'copy'):
2042-
logger.info('Pointing ' + repr(filename) + ' to the consistent snapshot.')
2042+
logger.debug('Pointing ' + repr(filename) + ' to the consistent snapshot.')
20432043
shutil.copyfile(written_consistent_filename, written_filename)
20442044

20452045
elif (tuf.conf.CONSISTENT_METHOD == 'hard_link'):
20462046
logger.info('Hard linking ' + repr(written_consistent_filename))
20472047

2048-
# 'written_filename' must not exist, otherwise os.link complains.
2048+
# 'written_filename' must not exist, otherwise os.link() complains.
20492049
if os.path.exists(written_filename):
20502050
os.remove(written_filename)
2051+
2052+
else:
2053+
logger.debug(repr(written_filename) + ' does not exist.')
20512054

20522055
os.link(written_consistent_filename, written_filename)
20532056

@@ -2150,7 +2153,7 @@ def _write_compressed_metadata(file_object, compressed_filename,
21502153
# Move the 'tuf.util.TempFile' object to one of the filenames so that it is
21512154
# saved and the temporary file closed.
21522155
if not os.path.exists(consistent_filename):
2153-
logger.info('Saving ' + repr(consistent_filename))
2156+
logger.debug('Saving ' + repr(consistent_filename))
21542157
file_object.move(consistent_filename)
21552158

21562159
else:
@@ -2201,6 +2204,16 @@ def _log_status_of_top_level_roles(targets_directory, metadata_directory):
22012204
# Do the top-level roles contain a valid threshold of signatures? Top-level
22022205
# metadata is verified in Root -> Targets -> Snapshot -> Timestamp order.
22032206
# Verify the metadata of the Root role.
2207+
dirty_rolenames = tuf.roledb.get_dirty_roles()
2208+
2209+
root_roleinfo = tuf.roledb.get_roleinfo('root')
2210+
root_is_dirty = None
2211+
if 'root' in dirty_rolenames:
2212+
root_is_dirty = True
2213+
2214+
else:
2215+
root_is_dirty = False
2216+
22042217
try:
22052218
signable, root_filename = \
22062219
_generate_and_write_metadata('root', root_filename,
@@ -2213,7 +2226,20 @@ def _log_status_of_top_level_roles(targets_directory, metadata_directory):
22132226
_log_status('root', e.signable)
22142227
return
22152228

2229+
finally:
2230+
tuf.roledb.unmark_dirty(['root'])
2231+
tuf.roledb.update_roleinfo('root', root_roleinfo,
2232+
mark_role_as_dirty=root_is_dirty)
2233+
22162234
# Verify the metadata of the Targets role.
2235+
targets_roleinfo = tuf.roledb.get_roleinfo('targets')
2236+
targets_is_dirty = None
2237+
if 'targets' in dirty_rolenames:
2238+
targets_is_dirty = True
2239+
2240+
else:
2241+
targets_is_dirty = False
2242+
22172243
try:
22182244
signable, targets_filename = \
22192245
_generate_and_write_metadata('targets', targets_filename,
@@ -2223,8 +2249,21 @@ def _log_status_of_top_level_roles(targets_directory, metadata_directory):
22232249
except tuf.UnsignedMetadataError as e:
22242250
_log_status('targets', e.signable)
22252251
return
2252+
2253+
finally:
2254+
tuf.roledb.unmark_dirty(['targets'])
2255+
tuf.roledb.update_roleinfo('targets', targets_roleinfo,
2256+
mark_role_as_dirty=targets_is_dirty)
22262257

22272258
# Verify the metadata of the snapshot role.
2259+
snapshot_roleinfo = tuf.roledb.get_roleinfo('snapshot')
2260+
snapshot_is_dirty = None
2261+
if 'snapshot' in dirty_rolenames:
2262+
snapshot_is_dirty = True
2263+
2264+
else:
2265+
snapshot_is_dirty = False
2266+
22282267
filenames = {'root': root_filename, 'targets': targets_filename}
22292268
try:
22302269
signable, snapshot_filename = \
@@ -2237,7 +2276,20 @@ def _log_status_of_top_level_roles(targets_directory, metadata_directory):
22372276
_log_status('snapshot', e.signable)
22382277
return
22392278

2279+
finally:
2280+
tuf.roledb.unmark_dirty(['snapshot'])
2281+
tuf.roledb.update_roleinfo('snapshot', snapshot_roleinfo,
2282+
mark_role_as_dirty=snapshot_is_dirty)
2283+
22402284
# Verify the metadata of the Timestamp role.
2285+
timestamp_roleinfo = tuf.roledb.get_roleinfo('timestamp')
2286+
timestamp_is_dirty = None
2287+
if 'timestamp' in dirty_rolenames:
2288+
timestamp_is_dirty = True
2289+
2290+
else:
2291+
timestamp_is_dirty = False
2292+
22412293
filenames = {'snapshot': snapshot_filename}
22422294
try:
22432295
signable, timestamp_filename = \
@@ -2249,8 +2301,12 @@ def _log_status_of_top_level_roles(targets_directory, metadata_directory):
22492301
except tuf.UnsignedMetadataError as e:
22502302
_log_status('timestamp', e.signable)
22512303
return
2252-
2253-
2304+
2305+
finally:
2306+
tuf.roledb.unmark_dirty(['timestamp'])
2307+
tuf.roledb.update_roleinfo('timestamp', timestamp_roleinfo,
2308+
mark_role_as_dirty=timestamp_is_dirty)
2309+
22542310

22552311

22562312
def _log_status(rolename, signable):

tuf/repository_tool.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,9 @@ def writeall(self, consistent_snapshot=False, compression_algorithms=['gz']):
279279
self._targets_directory,
280280
self._metadata_directory,
281281
consistent_snapshot, filenames)
282-
282+
283+
tuf.roledb.unmark_dirty(dirty_rolenames)
284+
283285
# Delete the metadata of roles no longer in 'tuf.roledb'. Obsolete roles
284286
# may have been revoked and should no longer have their metadata files
285287
# available on disk, otherwise loading a repository may unintentionally
@@ -339,6 +341,9 @@ def write(self, rolename, consistent_snapshot=False, increment_version_number=Tr
339341
filenames=filenames,
340342
allow_partially_signed=True,
341343
increment_version_number=increment_version_number)
344+
345+
# Ensure 'rolename' is no longer marked as dirty after the successful write().
346+
tuf.roledb.unmark_dirty([rolename])
342347

343348

344349

@@ -439,6 +444,30 @@ def mark_dirty(self, roles):
439444
"""
440445

441446
tuf.roledb.mark_dirty(roles)
447+
448+
449+
450+
def unmark_dirty(self, roles):
451+
"""
452+
<Purpose>
453+
No longer mark the list of 'roles' as dirty.
454+
455+
<Arguments>
456+
roles:
457+
A list of roles to mark as dirty. on the next write, these roles
458+
will be written to disk.
459+
460+
<Exceptions>
461+
None.
462+
463+
<Side Effects>
464+
None.
465+
466+
<Returns>
467+
None.
468+
"""
469+
470+
tuf.roledb.unmark_dirty(roles)
442471

443472

444473

tuf/roledb.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,51 @@ def mark_dirty(roles, repository_name='default'):
471471

472472

473473

474+
def unmark_dirty(roles, repository_name='default'):
475+
"""
476+
<Purpose>
477+
No longer mark the roles in 'roles' as dirty.
478+
479+
<Arguments>
480+
repository_name:
481+
The name of the repository to get the dirty roles. If not supplied, the
482+
'default' repository is searched.
483+
484+
roles:
485+
A list of roles that should no longer be marked as dirty.
486+
487+
<Exceptions>
488+
tuf.FormatError, if the arguments are improperly formatted.
489+
490+
tuf.InvalidNameError, if 'repository_name' does not exist in the role
491+
database.
492+
493+
<Side Effects>
494+
None.
495+
496+
<Returns>
497+
None.
498+
"""
499+
500+
# Are the arguments properly formatted? If not, raise tuf.FormatError.
501+
tuf.formats.NAMES_SCHEMA.check_match(roles)
502+
tuf.formats.NAME_SCHEMA.check_match(repository_name)
503+
504+
global _roledb_dict
505+
global _dirty_roles
506+
507+
if repository_name not in _roledb_dict or repository_name not in _dirty_roles:
508+
raise tuf.InvalidNameError('Repository name does not exist: ' + repository_name)
509+
510+
for role in roles:
511+
try:
512+
_dirty_roles[repository_name].remove(role)
513+
514+
except (KeyError, ValueError):
515+
logger.debug(repr(role) + ' is not dirty.')
516+
517+
518+
474519
def role_exists(rolename, repository_name='default'):
475520
"""
476521
<Purpose>

0 commit comments

Comments
 (0)