blob: e88b7c0453fcfcbd617bf14d24f3ce75243c5bfe [file] [log] [blame]
# 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 py_utils import cloud_storage
from 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:
#pylint: disable=nonstandard-exception
raise exceptions.CloudStorageUploadConflictError(self._cs_bucket,
self._cs_remote_path)
#pylint: enable=nonstandard-exception
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)