From 6de2740f0bbaba9a488e71c974b5093348e274ea Mon Sep 17 00:00:00 2001 From: Renata Date: Fri, 21 Dec 2018 13:24:09 +0100 Subject: [PATCH] Some refactoring of the updater class. In order to make the process of downloading metadata files more flexible, finding mirrors and the actual download were moved to a so-called handler class. With that change, it is possible to load a file from a specific commit of a git repository for example Signed-off-by: Renata --- tuf/client/handlers.py | 158 +++++++++++++++++++++++++++++++++++++++++ tuf/client/updater.py | 16 +++-- 2 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 tuf/client/handlers.py diff --git a/tuf/client/handlers.py b/tuf/client/handlers.py new file mode 100644 index 0000000000..d7b81bad74 --- /dev/null +++ b/tuf/client/handlers.py @@ -0,0 +1,158 @@ +import tuf +import logging +import tuf.exceptions + +logger = logging.getLogger('tuf.client.updater') + + +class MetadataUpdater(object): + """ + + Provide a way to redefine certain parts of the process of updating metadata. + To be more specific, this class should enable redefinition of how metadata + is downloaded. + + + + mirrors: + A dictionary holding repository mirror information, conformant to + 'tuf.formats.MIRRORDICT_SCHEMA'. + + repository_directory: + Client's repository directory. Specified via tuf.settings.repositories_directory. + + + + None. + + + None. + + + None. + """ + def __init__(self, mirrors, repository_directory): + self.mirrors = mirrors + self.repository_directory = repository_directory + +class RemoteMetadataUpdater(MetadataUpdater): + """ + Subclass of 'MetadataUpdater' which handles the case of + downloading metadata files from remote mirrors. + """ + + + def get_mirrors(self, remote_filename): + """ + + Finds mirrors from which the specified file can be downloaded. + + + + remote_filename: + The relative file path (on the remote repository) of a metadata role. + + + + None. + + Side Effects> + None. + + + A list of mirrors from which the specified file can be downloaded. + """ + return tuf.mirrors.get_list_of_mirrors('meta', remote_filename, + self.mirrors) + + + def get_metadata_file(self, file_mirror, _filename, _upperbound_filelength): + """ + + Downloads the metadata file from the provided mirror. Calls 'unsafe_download', which, + given the 'url' and 'required_length' of the desired file downloads the file and + returns its contents. + + + + file_mirror: + Mirror from which the file should be downloaded. + + _filename: + The relative file path (on the remote repository) of a metadata role. + + _upperbound_filelength: + An integer value representing the upper limit of the length of the file. + + + tuf.ssl_commons.exceptions.DownloadLengthMismatchError, if there was a + mismatch of observed vs expected lengths while downloading the file. + + securesystemslib.exceptions.FormatError, if any of the arguments are + improperly formatted. + + Any other unforeseen runtime exception. + + Side Effects> + A 'securesystemslib.util.TempFile' object is created on disk to store the + contents of 'url'. + + + A 'securesystemslib.util.TempFile' file-like object that points to the + contents of 'url'. + """ + return tuf.download.unsafe_download(file_mirror, + _upperbound_filelength) + + + def on_successful_update(self, filename, mirror): + """ + + React to successful update of a metadata file 'filename'. Called + after file 'filename' is downloaded from 'mirror' and all + validation checks pass. In this case, nothing needs to be done, + so the method is empty. + + + + filename: + The relative file path (on the remote repository) of a metadata role. + + mirror: + The mirror from whih th file was successfully downloaded. + + + + None. + + Side Effects> + None. + + + None. + """ + + + + def on_unsuccessful_update(self, filename): + """ + + React to unsuccessful update of a metadata file 'filename'. Called + after all attempts to download file 'filename' fail. + In this case, nothing needs to be done, so the method is empty. + + + + filename: + The relative file path (on the remote repository) of a metadata role. + + + + None. + + Side Effects> + None. + + + None + """ diff --git a/tuf/client/updater.py b/tuf/client/updater.py index dc4de93b04..3cb72eab31 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -139,6 +139,7 @@ import tuf.roledb import tuf.sig import tuf.exceptions +import tuf.client.handlers as handlers import securesystemslib.hash import securesystemslib.keys @@ -629,7 +630,8 @@ class Updater(object): http://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables """ - def __init__(self, repository_name, repository_mirrors): + def __init__(self, repository_name, repository_mirrors, + update_handler_cls=handlers.RemoteMetadataUpdater): """ Constructor. Instantiating an updater object causes all the metadata @@ -737,6 +739,7 @@ def __init__(self, repository_name, repository_mirrors): repositories_directory = tuf.settings.repositories_directory repository_directory = os.path.join(repositories_directory, self.repository_name) current_path = os.path.join(repository_directory, 'metadata', 'current') + self.update_handler = update_handler_cls(repository_mirrors, repository_directory) # Ensure the current path is valid/exists before saving it. if not os.path.exists(current_path): @@ -1472,17 +1475,17 @@ def _get_metadata_file(self, metadata_role, remote_filename, metadata. """ - file_mirrors = tuf.mirrors.get_list_of_mirrors('meta', remote_filename, - self.mirrors) + file_mirrors = self.update_handler.get_mirrors(remote_filename) # file_mirror (URL): error (Exception) file_mirror_errors = {} file_object = None + successful_mirror = None for file_mirror in file_mirrors: try: - file_object = tuf.download.unsafe_download(file_mirror, - upperbound_filelength) + file_object = self.update_handler.get_metadata_file(file_mirror, + remote_filename, upperbound_filelength) # Verify 'file_object' according to the callable function. # 'file_object' is also verified if decompressed above (i.e., the @@ -1549,12 +1552,15 @@ def _get_metadata_file(self, metadata_role, remote_filename, file_object = None else: + successful_mirror = file_mirror break if file_object: + self.update_handler.on_successful_update(remote_filename, successful_mirror) return file_object else: + self.update_handler.on_unsuccessful_update(remote_filename) logger.error('Failed to update ' + repr(remote_filename) + ' from all' ' mirrors: ' + repr(file_mirror_errors)) raise tuf.exceptions.NoWorkingMirrorError(file_mirror_errors)