Skip to content

Commit 32b221c

Browse files
committed
Port to new securesystemslib w abstract filesystem
Switch to using the new abstract files and directories support in securesystemslib by taking an object which implements securesystemslib.storage.StorageBackendInterface in the Repository constructor, passed in by tuf.repository_tool.create_new_repository() and tuf.repository_tool.load_repository() The Updater class in tuf.client.updater does not specify a storage backend and instead allows the functions in securesystemslib to perform the default action of instantiating a LocalFilesystemBackend, that is the updater does not currently support abstract filesystem backends and always defaults to using local storage. Finall we drop support for tuf.settings.CONSISTENT_METHOD as it's not as clear how different copying modes should work when the details of the underlying storage are abstracted away. Signed-off-by: Joshua Lock <[email protected]>
1 parent cf05221 commit 32b221c

11 files changed

+170
-238
lines changed

tests/test_developer_tool.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import tuf.exceptions
3939

4040
import securesystemslib
41+
import securesystemslib.exceptions
4142

4243
from tuf.developer_tool import METADATA_DIRECTORY_NAME
4344
from tuf.developer_tool import TARGETS_DIRECTORY_NAME
@@ -188,7 +189,8 @@ def test_load_project(self):
188189

189190
# Test non-existent project filepath.
190191
nonexistent_path = os.path.join(local_tmp, 'nonexistent')
191-
self.assertRaises(IOError, developer_tool.load_project, nonexistent_path)
192+
self.assertRaises(securesystemslib.exceptions.StorageError,
193+
developer_tool.load_project, nonexistent_path)
192194

193195
# Copy the pregenerated metadata.
194196
project_data_filepath = os.path.join('repository_data', 'project')

tests/test_multiple_repositories_integration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def setUp(self):
164164
'targets_path': 'targets',
165165
'confined_target_dirs': ['']}}
166166

167-
self.repository_mirrors2 = {'mirror1': {'url_prefix': url_prefix2,
167+
self.repository_mirrors2 = {'mirror2': {'url_prefix': url_prefix2,
168168
'metadata_path': 'metadata',
169169
'targets_path': 'targets',
170170
'confined_target_dirs': ['']}}

tests/test_repository_lib.py

Lines changed: 29 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@
5252
import tuf.repository_tool as repo_tool
5353

5454
import securesystemslib
55+
import securesystemslib.exceptions
5556
import securesystemslib.rsa_keys
5657
import securesystemslib.interface
58+
import securesystemslib.storage
5759
import six
5860

5961
logger = logging.getLogger(__name__)
@@ -126,8 +128,9 @@ def test_import_rsa_privatekey_from_file(self):
126128
# Non-existent key file.
127129
nonexistent_keypath = os.path.join(temporary_directory,
128130
'nonexistent_keypath')
129-
self.assertRaises(IOError, repo_lib.import_rsa_privatekey_from_file,
130-
nonexistent_keypath, 'pw')
131+
self.assertRaises(securesystemslib.exceptions.StorageError,
132+
repo_lib.import_rsa_privatekey_from_file,
133+
nonexistent_keypath, 'pw')
131134

132135
# Invalid key file argument.
133136
invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile')
@@ -160,7 +163,8 @@ def test_import_ed25519_privatekey_from_file(self):
160163
# Non-existent key file.
161164
nonexistent_keypath = os.path.join(temporary_directory,
162165
'nonexistent_keypath')
163-
self.assertRaises(IOError, repo_lib.import_ed25519_privatekey_from_file,
166+
self.assertRaises(securesystemslib.exceptions.StorageError,
167+
repo_lib.import_ed25519_privatekey_from_file,
164168
nonexistent_keypath, 'pw')
165169

166170
# Invalid key file argument.
@@ -215,7 +219,7 @@ def test_get_metadata_filenames(self):
215219
'targets.json': os.path.join(metadata_directory, 'targets.json'),
216220
'snapshot.json': os.path.join(metadata_directory, 'snapshot.json'),
217221
'timestamp.json': os.path.join(metadata_directory, 'timestamp.json')}
218-
self.assertEqual(filenames, repo_lib.get_metadata_filenames())
222+
self.assertEqual(filenames, repo_lib.get_metadata_filenames(metadata_directory))
219223

220224

221225
# Test improperly formatted argument.
@@ -440,8 +444,9 @@ def test_generate_snapshot_metadata(self):
440444

441445
# Load a valid repository so that top-level roles exist in roledb and
442446
# generate_snapshot_metadata() has roles to specify in snapshot metadata.
447+
storage_backend = securesystemslib.storage.FilesystemBackend()
443448
repository = repo_tool.Repository(repository_directory, metadata_directory,
444-
targets_directory)
449+
targets_directory, storage_backend)
445450

446451
repository_junk = repo_tool.load_repository(repository_directory)
447452

@@ -458,26 +463,27 @@ def test_generate_snapshot_metadata(self):
458463
repo_lib.generate_snapshot_metadata(metadata_directory, version,
459464
expiration_date,
460465
targets_filename,
466+
storage_backend,
461467
consistent_snapshot=False)
462468
self.assertTrue(tuf.formats.SNAPSHOT_SCHEMA.matches(snapshot_metadata))
463469

464470

465471
# Test improperly formatted arguments.
466472
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_snapshot_metadata,
467473
3, version, expiration_date,
468-
targets_filename, consistent_snapshot=False)
474+
targets_filename, consistent_snapshot=False, storage_backend=storage_backend)
469475
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_snapshot_metadata,
470476
metadata_directory, '3', expiration_date,
471-
targets_filename, consistent_snapshot=False)
477+
targets_filename, storage_backend, consistent_snapshot=False)
472478
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_snapshot_metadata,
473479
metadata_directory, version, '3',
474-
targets_filename, consistent_snapshot=False)
480+
targets_filename, storage_backend, consistent_snapshot=False)
475481
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_snapshot_metadata,
476482
metadata_directory, version, expiration_date,
477-
3, consistent_snapshot=False)
483+
3, storage_backend, consistent_snapshot=False)
478484
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_snapshot_metadata,
479485
metadata_directory, version, expiration_date,
480-
targets_filename, 3)
486+
targets_filename, 3, storage_backend)
481487

482488

483489

@@ -599,85 +605,25 @@ def test_write_metadata_file(self):
599605
version_number = root_signable['signed']['version'] + 1
600606

601607
self.assertFalse(os.path.exists(output_filename))
608+
storage_backend = securesystemslib.storage.FilesystemBackend()
602609
repo_lib.write_metadata_file(root_signable, output_filename, version_number,
603-
consistent_snapshot=False)
610+
consistent_snapshot=False, storage_backend=storage_backend)
604611
self.assertTrue(os.path.exists(output_filename))
605612

606613
# Attempt to over-write the previously written metadata file. An exception
607614
# is not raised in this case, only a debug message is logged.
608615
repo_lib.write_metadata_file(root_signable, output_filename, version_number,
609-
consistent_snapshot=False)
610-
611-
# Try to write a consistent metadate file. An exception is not raised in
612-
# this case. For testing purposes, root.json should be a hard link to the
613-
# consistent metadata file. We should verify that root.json points to
614-
# the latest consistent files.
615-
tuf.settings.CONSISTENT_METHOD = 'hard_link'
616-
repo_lib.write_metadata_file(root_signable, output_filename, version_number,
617-
consistent_snapshot=True)
618-
619-
# Test if the consistent files are properly named
620-
# Filename format of a consistent file: <version number>.rolename.json
621-
version_and_filename = str(version_number) + '.' + 'root.json'
622-
first_version_output_file = os.path.join(temporary_directory, version_and_filename)
623-
self.assertTrue(os.path.exists(first_version_output_file))
624-
625-
# Verify that the consistent file content is equal to 'output_filename'.
626-
self.assertEqual(
627-
securesystemslib.util.get_file_details(output_filename),
628-
securesystemslib.util.get_file_details(first_version_output_file))
629-
630-
# Try to add more consistent metadata files.
631-
version_number += 1
632-
root_signable['signed']['version'] = version_number
633-
repo_lib.write_metadata_file(root_signable, output_filename,
634-
version_number, consistent_snapshot=True)
635-
636-
# Test if the latest root.json points to the expected consistent file
637-
# and consistent metadata do not all point to the same root.json
638-
version_and_filename = str(version_number) + '.' + 'root.json'
639-
second_version_output_file = os.path.join(temporary_directory, version_and_filename)
640-
self.assertTrue(os.path.exists(second_version_output_file))
641-
642-
# Verify that the second version is equal to the second output file, and
643-
# that the second output filename differs from the first.
644-
self.assertEqual(securesystemslib.util.get_file_details(output_filename),
645-
securesystemslib.util.get_file_details(second_version_output_file))
646-
self.assertNotEqual(securesystemslib.util.get_file_details(output_filename),
647-
securesystemslib.util.get_file_details(first_version_output_file))
648-
649-
# Test for an improper settings.CONSISTENT_METHOD string value.
650-
tuf.settings.CONSISTENT_METHOD = 'somebadidea'
651-
652-
# Test for invalid consistent methods on systems other than Windows,
653-
# which always uses the copy method.
654-
if platform.system() == 'Windows':
655-
pass
656-
657-
else:
658-
self.assertRaises(securesystemslib.exceptions.InvalidConfigurationError,
659-
repo_lib.write_metadata_file, root_signable, output_filename,
660-
version_number, consistent_snapshot=True)
661-
662-
# Try to create a link to root.json when root.json doesn't exist locally.
663-
# repository_lib should log a message if this is the case.
664-
tuf.settings.CONSISTENT_METHOD = 'hard_link'
665-
os.remove(output_filename)
666-
repo_lib.write_metadata_file(root_signable, output_filename, version_number,
667-
consistent_snapshot=True)
668-
669-
# Reset CONSISTENT_METHOD so that subsequent tests work as expected.
670-
tuf.settings.CONSISTENT_METHOD = 'copy'
616+
consistent_snapshot=False, storage_backend=storage_backend)
671617

672618
# Test improperly formatted arguments.
673619
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.write_metadata_file,
674-
3, output_filename, version_number, False)
620+
3, output_filename, version_number, False, storage_backend)
675621
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.write_metadata_file,
676-
root_signable, 3, version_number, False)
622+
root_signable, 3, version_number, False, storage_backend)
677623
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.write_metadata_file,
678-
root_signable, output_filename, '3', False)
624+
root_signable, output_filename, '3', False, storage_backend)
679625
self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.write_metadata_file,
680-
root_signable, output_filename, version_number, 3)
626+
root_signable, output_filename, storage_backend, version_number, 3)
681627

682628

683629

@@ -774,9 +720,11 @@ def test__generate_and_write_metadata(self):
774720
tuf.roledb.add_role('obsolete_role', targets_roleinfo,
775721
repository_name=repository_name)
776722

723+
storage_backend = securesystemslib.storage.FilesystemBackend()
777724
repo_lib._generate_and_write_metadata('obsolete_role', obsolete_metadata,
778-
targets_directory, metadata_directory, consistent_snapshot=False,
779-
filenames=None, repository_name=repository_name)
725+
targets_directory, metadata_directory, storage_backend,
726+
consistent_snapshot=False, filenames=None,
727+
repository_name=repository_name)
780728

781729
snapshot_filepath = os.path.join('repository_data', 'repository',
782730
'metadata', 'snapshot.json')
@@ -843,7 +791,8 @@ def test__load_top_level_metadata(self):
843791
signable = securesystemslib.util.load_json_file(os.path.join(metadata_directory, 'root.json'))
844792
signable['signatures'].append(signable['signatures'][0])
845793

846-
repo_lib.write_metadata_file(signable, root_file, 8, False)
794+
storage_backend = securesystemslib.storage.FilesystemBackend()
795+
repo_lib.write_metadata_file(signable, root_file, 8, False, storage_backend)
847796

848797
# Attempt to load a repository that contains a compressed Root file.
849798
repository = repo_tool.create_new_repository(repository_directory, repository_name)

tests/test_repository_tool.py

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import securesystemslib.exceptions
4949

5050
import securesystemslib
51+
import securesystemslib.storage
5152
import six
5253

5354
logger = logging.getLogger(__name__)
@@ -96,20 +97,22 @@ def tearDown(self):
9697
def test_init(self):
9798
# Test normal case.
9899
repository_name = 'test_repository'
100+
storage_backend = securesystemslib.storage.FilesystemBackend()
99101
repository = repo_tool.Repository('repository_directory/',
100-
'metadata_directory/', 'targets_directory/', repository_name)
102+
'metadata_directory/', 'targets_directory/', storage_backend,
103+
repository_name)
101104
self.assertTrue(isinstance(repository.root, repo_tool.Root))
102105
self.assertTrue(isinstance(repository.snapshot, repo_tool.Snapshot))
103106
self.assertTrue(isinstance(repository.timestamp, repo_tool.Timestamp))
104107
self.assertTrue(isinstance(repository.targets, repo_tool.Targets))
105108

106109
# Test improperly formatted arguments.
107-
self.assertRaises(securesystemslib.exceptions.FormatError, repo_tool.Repository, 3,
108-
'metadata_directory/', 'targets_directory')
109110
self.assertRaises(securesystemslib.exceptions.FormatError, repo_tool.Repository,
110-
'repository_directory', 3, 'targets_directory')
111+
storage_backend, 3, 'metadata_directory/', 'targets_directory')
112+
self.assertRaises(securesystemslib.exceptions.FormatError, repo_tool.Repository,
113+
'repository_directory', storage_backend, 3, 'targets_directory')
111114
self.assertRaises(securesystemslib.exceptions.FormatError, repo_tool.Repository,
112-
'repository_directory', 'metadata_directory', 3)
115+
'repository_directory', 'metadata_directory', 3, storage_backend)
113116

114117

115118

@@ -1818,14 +1821,9 @@ def test_create_new_repository(self):
18181821
repo_tool.create_new_repository, 3, repository_name)
18191822

18201823
# For testing purposes, try to create a repository directory that
1821-
# fails due to a non-errno.EEXIST exception raised. create_new_repository()
1822-
# should only pass for OSError (errno.EEXIST).
1823-
try:
1824-
repo_tool.create_new_repository('bad' * 2000, repository_name)
1825-
1826-
except OSError as e:
1827-
# errno.ENOENT is raised in Windows.
1828-
self.assertTrue(e.errno == errno.ENAMETOOLONG or e.errno == errno.ENOENT)
1824+
# fails due to a non-errno.EEXIST exception raised.
1825+
self.assertRaises(securesystemslib.exceptions.StorageError,
1826+
repo_tool.create_new_repository, 'bad' * 2000, repository_name)
18291827

18301828
# Reset the 'repository_directory' so that the metadata and targets
18311829
# directories can be tested likewise.
@@ -1836,12 +1834,8 @@ def test_create_new_repository(self):
18361834
tuf.repository_tool.METADATA_STAGED_DIRECTORY_NAME
18371835
tuf.repository_tool.METADATA_STAGED_DIRECTORY_NAME = 'bad' * 2000
18381836

1839-
try:
1840-
repo_tool.create_new_repository(repository_directory, repository_name)
1841-
1842-
except OSError as e:
1843-
# errno.ENOENT is raised in Windows.
1844-
self.assertTrue(e.errno == errno.ENAMETOOLONG or e.errno == errno.ENOENT)
1837+
self.assertRaises(securesystemslib.exceptions.StorageError,
1838+
repo_tool.create_new_repository, repository_directory, repository_name)
18451839

18461840
# Reset metadata staged directory so that the targets directory can be
18471841
# tested...
@@ -1851,12 +1845,8 @@ def test_create_new_repository(self):
18511845
original_targets_directory = tuf.repository_tool.TARGETS_DIRECTORY_NAME
18521846
tuf.repository_tool.TARGETS_DIRECTORY_NAME = 'bad' * 2000
18531847

1854-
try:
1855-
repo_tool.create_new_repository(repository_directory, repository_name)
1856-
1857-
except OSError as e:
1858-
# errno.ENOENT is raised in Windows.
1859-
self.assertTrue(e.errno == errno.ENAMETOOLONG or e.errno == errno.ENOENT)
1848+
self.assertRaises(securesystemslib.exceptions.StorageError,
1849+
repo_tool.create_new_repository, repository_directory, repository_name)
18601850

18611851
tuf.repository_tool.TARGETS_DIRECTORY_NAME = \
18621852
original_targets_directory
@@ -1943,8 +1933,10 @@ def test_dump_signable_metadata(self):
19431933
metadata_content = repo_tool.dump_signable_metadata(targets_metadata_file)
19441934

19451935
# Test for an invalid targets metadata file..
1946-
self.assertRaises(securesystemslib.exceptions.FormatError, repo_tool.dump_signable_metadata, 1)
1947-
self.assertRaises(IOError, repo_tool.dump_signable_metadata, 'bad file path')
1936+
self.assertRaises(securesystemslib.exceptions.FormatError,
1937+
repo_tool.dump_signable_metadata, 1)
1938+
self.assertRaises(securesystemslib.exceptions.StorageError,
1939+
repo_tool.dump_signable_metadata, 'bad file path')
19481940

19491941

19501942

tests/test_root_versioning_integration.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import tuf.repository_tool as repo_tool
4242

4343
import securesystemslib
44+
import securesystemslib.storage
4445

4546
logger = logging.getLogger(__name__)
4647

@@ -63,21 +64,23 @@ def tearDown(self):
6364

6465
def test_init(self):
6566
# Test normal case.
67+
storage_backend = securesystemslib.storage.FilesystemBackend()
6668
repository = repo_tool.Repository('repository_directory/',
6769
'metadata_directory/',
68-
'targets_directory/')
70+
'targets_directory/',
71+
storage_backend)
6972
self.assertTrue(isinstance(repository.root, repo_tool.Root))
7073
self.assertTrue(isinstance(repository.snapshot, repo_tool.Snapshot))
7174
self.assertTrue(isinstance(repository.timestamp, repo_tool.Timestamp))
7275
self.assertTrue(isinstance(repository.targets, repo_tool.Targets))
7376

7477
# Test improperly formatted arguments.
7578
self.assertRaises(securesystemslib.exceptions.FormatError, repo_tool.Repository, 3,
76-
'metadata_directory/', 'targets_directory')
79+
'metadata_directory/', 'targets_directory', storage_backend)
7780
self.assertRaises(securesystemslib.exceptions.FormatError, repo_tool.Repository,
78-
'repository_directory', 3, 'targets_directory')
81+
'repository_directory', 3, 'targets_directory', storage_backend)
7982
self.assertRaises(securesystemslib.exceptions.FormatError, repo_tool.Repository,
80-
'repository_directory', 'metadata_directory', 3)
83+
'repository_directory', 'metadata_directory', storage_backend, 3)
8184

8285

8386

tests/test_updater.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ def test_1__refresh_must_not_count_duplicate_keyids_towards_threshold(self):
431431
repository.root.threshold = 2
432432
repository.root.load_signing_key(self.role_keys['root']['private'])
433433

434+
storage_backend = securesystemslib.storage.FilesystemBackend()
434435
# The client uses the threshold from the previous root file to verify the
435436
# new root. Thus we need to make two updates so that the threshold used for
436437
# verification becomes 2. I.e. we bump the version, sign twice with the
@@ -450,7 +451,8 @@ def test_1__refresh_must_not_count_duplicate_keyids_towards_threshold(self):
450451
# catch the unmet threshold.
451452
# We also skip writing to 'metadata.staged' and copying to 'metadata' and
452453
# instead write directly to 'metadata'
453-
repo_lib.write_metadata_file(signed_metadata, live_root_path, info["version"], True)
454+
repo_lib.write_metadata_file(signed_metadata, live_root_path,
455+
info["version"], True, storage_backend)
454456

455457

456458
# Update from current '1.root.json' to '3.root.json' on client and assert

0 commit comments

Comments
 (0)