# -*- coding: utf-8 -*- # # Copyright 2013 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # 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. """Command to list Cloud Storage objects.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.api_lib.storage import storage_api from googlecloudsdk.api_lib.storage import storage_util from googlecloudsdk.calliope import base from googlecloudsdk.calliope import exceptions from googlecloudsdk.command_lib.storage import expansion from googlecloudsdk.command_lib.storage import flags from googlecloudsdk.command_lib.storage import storage_parallel from googlecloudsdk.core import log from googlecloudsdk.core.console import console_io @base.Hidden @base.Deprecate(is_removed=False, warning='This command is deprecated. ' 'Use `gcloud alpha storage rm` instead.') @base.UniverseCompatible @base.ReleaseTracks(base.ReleaseTrack.ALPHA) class Delete(base.Command): """Delete Cloud Storage objects and buckets.""" detailed_help = { 'DESCRIPTION': """\ *{command}* lets you delete Cloud Storage objects and buckets. You can specify one or more paths (including wildcards) and all matching objects and buckets will be deleted. """, 'EXAMPLES': """\ To delete an object, run: $ *{command}* gs://mybucket/a.txt To delete all objects in a directory, run: $ *{command}* gs://mybucket/remote-dir/* The above command will delete all objects under remote-dir/ but not its sub-directories. To delete a directory and all its objects and subdirectories, run: $ *{command}* --recursive gs://mybucket/remote-dir $ *{command}* gs://mybucket/remote-dir/** To delete all objects and subdirectories of a directory, without deleting the directory itself, run: $ *{command}* --recursive gs://mybucket/remote-dir/* or $ *{command}* gs://mybucket/remote-dir/** To delete all objects and directories in a bucket without deleting the bucket itself, run: $ *{command}* gs://mybucket/** To delete all text files in a bucket or a directory, run: $ *{command}* gs://mybucket/*.txt $ *{command}* gs://mybucket/remote-dir/*.txt To go beyond directory boundary and delete all text files in a bucket or a directory, run: $ *{command}* gs://mybucket/**/*.txt $ *{command}* gs://mybucket/remote-dir/**/*.txt To delete a bucket, run: $ *{command}* gs://mybucket You can use wildcards in bucket names. To delete all buckets with prefix of `my`, run: $ *{command}* --recursive gs://my* """, } @staticmethod def Args(parser): parser.add_argument( 'path', nargs='+', help='The path of objects and directories to delete. The path must ' 'begin with gs:// and may or may not contain wildcard characters.') parser.add_argument( '--recursive', action='store_true', help='Recursively delete the contents of any directories that match ' 'the path expression.') parser.add_argument( '--num-threads', type=int, hidden=True, default=16, help='The number of threads to use for the delete.') flags.add_additional_headers_flag(parser) def Run(self, args): paths = args.path or ['gs://'] expander = expansion.GCSPathExpander() objects, dirs = expander.ExpandPaths(paths) if dirs and not args.recursive: raise exceptions.RequiredArgumentException( '--recursive', 'Source path matches directories but --recursive was not specified.') buckets = [] dir_paths = [] for d in dirs: obj_ref = storage_util.ObjectReference.FromUrl(d, allow_empty_object=True) if not obj_ref.name: buckets.append(obj_ref.bucket_ref) dir_paths.append(d + '**') sub_objects, _ = expander.ExpandPaths(dir_paths) objects.update(sub_objects) tasks = [] for o in sorted(objects): tasks.append(storage_parallel.ObjectDeleteTask( storage_util.ObjectReference.FromUrl(o))) if buckets: # Extra warnings and confirmation if any buckets will be deleted. log.warning('Deleting a bucket is irreversible and makes that bucket ' 'name available for others to claim.') message = 'This command will delete the following buckets:\n ' message += '\n '.join([b.bucket for b in buckets]) console_io.PromptContinue( message=message, throw_if_unattended=True, cancel_on_no=True) # TODO(b/120033753): Handle long lists of items. message = 'You are about to delete the following:' message += ''.join(['\n ' + b.ToUrl() for b in buckets]) message += ''.join(['\n ' + t.obj_ref.ToUrl() for t in tasks]) console_io.PromptContinue( message=message, throw_if_unattended=True, cancel_on_no=True) storage_parallel.ExecuteTasks(tasks, num_threads=args.num_threads, progress_bar_label='Deleting Files') log.status.write( 'Deleted [{}] file{}.\n'.format( len(tasks), 's' if len(tasks) > 1 else '')) storage_client = storage_api.StorageClient() for b in buckets: storage_client.DeleteBucket(b) log.DeletedResource(b.ToUrl(), kind='bucket')