Index: tools/telemetry/catapult_base/dependency_manager/uploader.py |
diff --git a/tools/telemetry/catapult_base/dependency_manager/uploader.py b/tools/telemetry/catapult_base/dependency_manager/uploader.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..08e9c8f6b35d5159a1af8a40cf240a8e92de1e84 |
--- /dev/null |
+++ b/tools/telemetry/catapult_base/dependency_manager/uploader.py |
@@ -0,0 +1,106 @@ |
+# Copyright 2015 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import logging |
+import os |
+ |
+from catapult_base import cloud_storage |
+ |
+from catapult_base.dependency_manager import exceptions |
+ |
+ |
+BACKUP_PATH_EXTENSION = 'old' |
+ |
+ |
+class CloudStorageUploader(object): |
+ def __init__(self, bucket, remote_path, local_path, cs_backup_path=None): |
+ if not bucket or not remote_path or not local_path: |
+ raise ValueError( |
+ 'Attempted to partially initialize upload data with bucket %s, ' |
+ 'remote_path %s, and local_path %s', bucket, remote_path, local_path) |
+ if not os.path.exists(local_path): |
+ raise ValueError('Attempting to initilize UploadInfo with missing ' |
+ 'local path %s', local_path) |
+ |
+ self._cs_bucket = bucket |
+ self._cs_remote_path = remote_path |
+ self._local_path = local_path |
+ self._cs_backup_path = (cs_backup_path or |
+ '%s.%s' % (self._cs_remote_path, |
+ BACKUP_PATH_EXTENSION)) |
+ self._updated = False |
+ self._backed_up = False |
+ |
+ def Upload(self, force=False): |
+ """Upload all pending files and then write the updated config to disk. |
+ |
+ Will attempt to copy files existing in the upload location to a backup |
+ location in the same bucket in cloud storage if |force| is True. |
+ |
+ Args: |
+ force: True if files should be uploaded to cloud storage even if a |
+ file already exists in the upload location. |
+ |
+ Raises: |
+ CloudStorageUploadConflictError: If |force| is False and the potential |
+ upload location of a file already exists. |
+ CloudStorageError: If copying an existing file to the backup location |
+ or uploading the new file fails. |
+ """ |
+ if cloud_storage.Exists(self._cs_bucket, self._cs_remote_path): |
+ if not force: |
+ raise exceptions.CloudStorageUploadConflictError(self._cs_bucket, |
+ self._cs_remote_path) |
+ logging.debug('A file already exists at upload path %s in self.cs_bucket' |
+ ' %s', self._cs_remote_path, self._cs_bucket) |
+ try: |
+ cloud_storage.Copy(self._cs_bucket, self._cs_bucket, |
+ self._cs_remote_path, self._cs_backup_path) |
+ self._backed_up = True |
+ except cloud_storage.CloudStorageError: |
+ logging.error('Failed to copy existing file %s in cloud storage bucket ' |
+ '%s to backup location %s', self._cs_remote_path, self._cs_bucket, |
+ self._cs_backup_path) |
+ raise |
+ |
+ try: |
+ cloud_storage.Insert( |
+ self._cs_bucket, self._cs_remote_path, self._local_path) |
+ except cloud_storage.CloudStorageError: |
+ logging.error('Failed to upload %s to %s in cloud_storage bucket %s', |
+ self._local_path, self._cs_remote_path, self._cs_bucket) |
+ raise |
+ self._updated = True |
+ |
+ def Rollback(self): |
+ """Attempt to undo the previous call to Upload. |
+ |
+ Does nothing if no previous call to Upload was made, or if nothing was |
+ successfully changed. |
+ |
+ Returns: |
+ True iff changes were successfully rolled back. |
+ Raises: |
+ CloudStorageError: If copying the backed up file to its original |
+ location or removing the uploaded file fails. |
+ """ |
+ cloud_storage_changed = False |
+ if self._backed_up: |
+ cloud_storage.Copy(self._cs_bucket, self._cs_bucket, self._cs_backup_path, |
+ self._cs_remote_path) |
+ cloud_storage_changed = True |
+ self._cs_backup_path = None |
+ elif self._updated: |
+ cloud_storage.Delete(self._cs_bucket, self._cs_remote_path) |
+ cloud_storage_changed = True |
+ self._updated = False |
+ return cloud_storage_changed |
+ |
+ def __eq__(self, other, msg=None): |
+ if type(self) != type(other): |
+ return False |
+ return (self._local_path == other._local_path and |
+ self._cs_remote_path == other._cs_remote_path and |
+ self._cs_bucket == other._cs_bucket) |
+ |