Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(131)

Unified Diff: gslib/commands/rm.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gslib/commands/rb.py ('k') | gslib/commands/rsync.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gslib/commands/rm.py
===================================================================
--- gslib/commands/rm.py (revision 33376)
+++ gslib/commands/rm.py (working copy)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright 2011 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,33 +12,27 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+"""Implementation of Unix-like rm command for cloud storage providers."""
-import boto
-import textwrap
+from __future__ import absolute_import
-from boto.exception import GSResponseError
+from gslib.cloud_api import NotEmptyException
+from gslib.cloud_api import ServiceException
from gslib.command import Command
-from gslib.command import COMMAND_NAME
-from gslib.command import COMMAND_NAME_ALIASES
-from gslib.command import FILE_URIS_OK
-from gslib.command import MAX_ARGS
-from gslib.command import MIN_ARGS
-from gslib.command import PROVIDER_URIS_OK
-from gslib.command import SUPPORTED_SUB_ARGS
-from gslib.command import URIS_START_ARG
+from gslib.command import GetFailureCount
+from gslib.command import ResetFailureCount
+from gslib.cs_api_map import ApiSelector
from gslib.exception import CommandException
-from gslib.help_provider import HELP_NAME
-from gslib.help_provider import HELP_NAME_ALIASES
-from gslib.help_provider import HELP_ONE_LINE_SUMMARY
-from gslib.help_provider import HELP_TEXT
-from gslib.help_provider import HelpType
-from gslib.help_provider import HELP_TYPE
from gslib.name_expansion import NameExpansionIterator
+from gslib.storage_url import StorageUrlFromString
+from gslib.util import GetCloudApiInstance
from gslib.util import NO_MAX
+from gslib.util import Retry
-_detailed_help_text = ("""
+
+_DETAILED_HELP_TEXT = ("""
<B>SYNOPSIS</B>
- gsutil rm [-f] [-R] uri...
+ gsutil rm [-f] [-R] url...
<B>DESCRIPTION</B>
@@ -61,9 +56,13 @@
gsutil rm gs://bucket/subdir**
gsutil rm -R gs://bucket/subdir
- Running gsutil rm -R on a bucket will delete all objects in the bucket, and
- then delete the bucket:
+ The -R option will also delete all object versions in the subdirectory for
+ versioning-enabled buckets, whereas the ** command will only delete the live
+ version of each object in the subdirectory.
+ Running gsutil rm -R on a bucket will delete all versions of all objects in
+ the bucket, and then delete the bucket:
+
gsutil rm -R gs://bucket
If you want to delete all objects in the bucket, but not the bucket itself,
@@ -91,65 +90,72 @@
<B>OPTIONS</B>
-f Continues silently (without printing error messages) despite
- errors when removing multiple objects. With this option the gsutil
- exit status will be 0 even if some objects couldn't be removed.
+ errors when removing multiple objects. If some of the objects
+ could not be removed, gsutil's exit status will be non-zero even
+ if this flag is set. This option is implicitly set when running
+ "gsutil -m rm ...".
- -R, -r Causes bucket contents to be removed recursively (i.e., including
- all objects and subdirectories). If used with a bucket-only URI
- (like gs://bucket), after deleting objects and subdirectories
- gsutil will delete the bucket.
+ -R, -r Causes bucket or bucket subdirectory contents (all objects and
+ subdirectories that it contains) to be removed recursively. If
+ used with a bucket-only URL (like gs://bucket), after deleting
+ objects and subdirectories gsutil will delete the bucket. The -r
+ flag implies the -a flag and will delete all object versions.
-a Delete all versions of an object.
""")
+
def _RemoveExceptionHandler(cls, e):
"""Simple exception handler to allow post-completion status."""
- cls.logger.error(str(e))
+ if not cls.continue_on_error:
+ cls.logger.error(str(e))
cls.everything_removed_okay = False
-
-def _RemoveFuncWrapper(cls, name_expansion_result):
- cls._RemoveFunc(name_expansion_result)
+# pylint: disable=unused-argument
+def _RemoveFoldersExceptionHandler(cls, e):
+ """When removing folders, we don't mind if none exist."""
+ if (isinstance(e, CommandException.__class__) and
+ 'No URLs matched' in e.message):
+ pass
+ else:
+ raise e
+
+
+def _RemoveFuncWrapper(cls, name_expansion_result, thread_state=None):
+ cls.RemoveFunc(name_expansion_result, thread_state=thread_state)
+
+
class RmCommand(Command):
"""Implementation of gsutil rm command."""
- # Command specification (processed by parent class).
- command_spec = {
- # Name of command.
- COMMAND_NAME : 'rm',
- # List of command name aliases.
- COMMAND_NAME_ALIASES : ['del', 'delete', 'remove'],
- # Min number of args required by this command.
- MIN_ARGS : 1,
- # Max number of args required by this command, or NO_MAX.
- MAX_ARGS : NO_MAX,
- # Getopt-style string specifying acceptable sub args.
- SUPPORTED_SUB_ARGS : 'afrRv',
- # True if file URIs acceptable for this command.
- FILE_URIS_OK : False,
- # True if provider-only URIs acceptable for this command.
- PROVIDER_URIS_OK : False,
- # Index in args of first URI arg.
- URIS_START_ARG : 0,
- }
- help_spec = {
- # Name of command or auxiliary help info for which this help applies.
- HELP_NAME : 'rm',
- # List of help name aliases.
- HELP_NAME_ALIASES : ['del', 'delete', 'remove'],
- # Type of help:
- HELP_TYPE : HelpType.COMMAND_HELP,
- # One line summary of this help.
- HELP_ONE_LINE_SUMMARY : 'Remove objects',
- # The full help text.
- HELP_TEXT : _detailed_help_text,
- }
+ # Command specification. See base class for documentation.
+ command_spec = Command.CreateCommandSpec(
+ 'rm',
+ command_name_aliases=['del', 'delete', 'remove'],
+ min_args=1,
+ max_args=NO_MAX,
+ supported_sub_args='afrR',
+ file_url_ok=False,
+ provider_url_ok=False,
+ urls_start_arg=0,
+ gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
+ gs_default_api=ApiSelector.JSON,
+ )
+ # Help specification. See help_provider.py for documentation.
+ help_spec = Command.HelpSpec(
+ help_name='rm',
+ help_name_aliases=['del', 'delete', 'remove'],
+ help_type='command_help',
+ help_one_line_summary='Remove objects',
+ help_text=_DETAILED_HELP_TEXT,
+ subcommand_help_text={},
+ )
- # Command entry point.
def RunCommand(self):
- # self.recursion_requested initialized in command.py (so can be checked
- # in parent class for all commands).
+ """Command entry point for the rm command."""
+ # self.recursion_requested is initialized in command.py (so it can be
+ # checked in parent class for all commands).
self.continue_on_error = False
self.all_versions = False
if self.sub_opts:
@@ -160,41 +166,31 @@
self.continue_on_error = True
elif o == '-r' or o == '-R':
self.recursion_requested = True
- elif o == '-v':
- self.logger.info('WARNING: The %s -v option is no longer'
- ' needed, and will eventually be removed.\n'
- % self.command_name)
+ self.all_versions = True
- if self.recursion_requested and not self.all_versions:
- for uri_str in self.args:
- # WildcardIterator returns BucketListingRefs.
- for blr in self.WildcardIterator(uri_str):
- uri = blr.GetUri()
- if uri.names_bucket() and uri.get_versioning_config():
- raise CommandException(
- 'Running gsutil rm -R on a bucket-only URI (%s)\nwith '
- 'versioning enabled will not work without specifying the -a '
- 'flag. Please try\nagain, using:\n\tgsutil rm -Ra %s'
- % (uri_str,' '.join(self.args)))
+ bucket_urls_to_delete = []
+ bucket_strings_to_delete = []
+ if self.recursion_requested:
+ bucket_fields = ['id']
+ for url_str in self.args:
+ url = StorageUrlFromString(url_str)
+ if url.IsBucket() or url.IsProvider():
+ for blr in self.WildcardIterator(url_str).IterBuckets(
+ bucket_fields=bucket_fields):
+ bucket_urls_to_delete.append(blr.storage_url)
+ bucket_strings_to_delete.append(url_str)
# Used to track if any files failed to be removed.
self.everything_removed_okay = True
- bucket_uris_to_delete = []
- if self.recursion_requested:
- for uri_str in self.args:
- for blr in self.WildcardIterator(uri_str):
- uri = blr.GetUri()
- if uri.names_bucket():
- bucket_uris_to_delete.append(uri)
-
try:
- # Expand wildcards, dirs, buckets, and bucket subdirs in URIs.
+ # Expand wildcards, dirs, buckets, and bucket subdirs in URLs.
name_expansion_iterator = NameExpansionIterator(
- self.command_name, self.proj_id_handler, self.headers, self.debug,
- self.logger, self.bucket_storage_uri_class, self.args,
- self.recursion_requested, flat=self.recursion_requested,
- all_versions=self.all_versions)
+ self.command_name, self.debug, self.logger, self.gsutil_api,
+ self.args, self.recursion_requested, project_id=self.project_id,
+ all_versions=self.all_versions,
+ continue_on_error=self.continue_on_error or self.parallel_operations)
+
# Perform remove requests in parallel (-m) mode, if requested, using
# configured number of parallel processes and threads. Otherwise,
# perform requests with sequential function calls in current process.
@@ -202,7 +198,7 @@
_RemoveExceptionHandler,
fail_on_error=(not self.continue_on_error))
- # Assuming the bucket has versioning enabled, uri's that don't map to
+ # Assuming the bucket has versioning enabled, url's that don't map to
# objects should throw an error even with all_versions, since the prior
# round of deletes only sends objects to a history table.
# This assumption that rm -a is only called for versioned buckets should be
@@ -211,9 +207,16 @@
# Don't raise if there are buckets to delete -- it's valid to say:
# gsutil rm -r gs://some_bucket
# if the bucket is empty.
- if not bucket_uris_to_delete and not self.continue_on_error:
+ if not bucket_urls_to_delete and not self.continue_on_error:
raise
- except GSResponseError, e:
+ # Reset the failure count if we failed due to an empty bucket that we're
+ # going to delete.
+ msg = 'No URLs matched: '
+ if msg in str(e):
+ parts = str(e).split(msg)
+ if len(parts) == 2 and parts[1] in bucket_strings_to_delete:
+ ResetFailureCount()
+ except ServiceException, e:
if not self.continue_on_error:
raise
@@ -224,43 +227,50 @@
# remove any dir_$folder$ objects (which are created by various web UI
# tools to simulate folders).
if self.recursion_requested:
+ had_previous_failures = GetFailureCount() > 0
folder_object_wildcards = []
- for uri_str in self.args:
- uri = self.suri_builder.StorageUri(uri_str)
- if uri.names_object:
- folder_object_wildcards.append('%s**_$folder$' % uri)
- if len(folder_object_wildcards):
+ for url_str in self.args:
+ url = StorageUrlFromString(url_str)
+ if url.IsObject():
+ folder_object_wildcards.append('%s**_$folder$' % url_str)
+ if folder_object_wildcards:
self.continue_on_error = True
try:
name_expansion_iterator = NameExpansionIterator(
- self.command_name, self.proj_id_handler, self.headers, self.debug,
- self.logger, self.bucket_storage_uri_class,
- folder_object_wildcards, self.recursion_requested, flat=True,
+ self.command_name, self.debug,
+ self.logger, self.gsutil_api,
+ folder_object_wildcards, self.recursion_requested,
+ project_id=self.project_id,
all_versions=self.all_versions)
+ # When we're removing folder objects, always continue on error
self.Apply(_RemoveFuncWrapper, name_expansion_iterator,
- _RemoveExceptionHandler,
- fail_on_error=(not self.continue_on_error))
+ _RemoveFoldersExceptionHandler,
+ fail_on_error=False)
except CommandException as e:
# Ignore exception from name expansion due to an absent folder file.
- if not e.reason.startswith('No URIs matched:'):
+ if not e.reason.startswith('No URLs matched:'):
raise
+ if not had_previous_failures:
+ ResetFailureCount()
- # Now that all data has been deleted, delete any bucket URIs.
- for uri in bucket_uris_to_delete:
- self.logger.info('Removing %s...', uri)
- uri.delete_bucket(self.headers)
+ # Now that all data has been deleted, delete any bucket URLs.
+ for url in bucket_urls_to_delete:
+ self.logger.info('Removing %s...', url)
+
+ @Retry(NotEmptyException, tries=3, timeout_secs=1)
+ def BucketDeleteWithRetry():
+ self.gsutil_api.DeleteBucket(url.bucket_name, provider=url.scheme)
+
+ BucketDeleteWithRetry()
+
return 0
- def _RemoveFunc(self, name_expansion_result):
- exp_src_uri = self.suri_builder.StorageUri(
- name_expansion_result.GetExpandedUriStr(),
- is_latest=name_expansion_result.is_latest)
-
- self.logger.info('Removing %s...', name_expansion_result.expanded_uri_str)
- try:
- exp_src_uri.delete_key(validate=False, headers=self.headers)
- except:
- if self.continue_on_error:
- self.everything_removed_okay = False
- else:
- raise
+ def RemoveFunc(self, name_expansion_result, thread_state=None):
+ gsutil_api = GetCloudApiInstance(self, thread_state=thread_state)
+
+ exp_src_url = name_expansion_result.expanded_storage_url
+ self.logger.info('Removing %s...', exp_src_url)
+ gsutil_api.DeleteObject(
+ exp_src_url.bucket_name, exp_src_url.object_name,
+ generation=exp_src_url.generation, provider=exp_src_url.scheme)
+
« no previous file with comments | « gslib/commands/rb.py ('k') | gslib/commands/rsync.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698