Home | History | Annotate | Download | only in commands
      1 # -*- coding: utf-8 -*-
      2 # Copyright 2013 Google Inc. All Rights Reserved.
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #     http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 """Implementation of Unix-like stat command for cloud storage providers."""
     16 
     17 from __future__ import absolute_import
     18 
     19 import logging
     20 import sys
     21 
     22 from gslib.bucket_listing_ref import BucketListingObject
     23 from gslib.cloud_api import AccessDeniedException
     24 from gslib.cloud_api import NotFoundException
     25 from gslib.command import Command
     26 from gslib.command_argument import CommandArgument
     27 from gslib.cs_api_map import ApiSelector
     28 from gslib.exception import CommandException
     29 from gslib.exception import InvalidUrlError
     30 from gslib.storage_url import ContainsWildcard
     31 from gslib.storage_url import StorageUrlFromString
     32 from gslib.util import NO_MAX
     33 from gslib.util import PrintFullInfoAboutObject
     34 
     35 
     36 _SYNOPSIS = """
     37   gsutil stat url...
     38 """
     39 
     40 _DETAILED_HELP_TEXT = ("""
     41 <B>SYNOPSIS</B>
     42 """ + _SYNOPSIS + """
     43 
     44 
     45 <B>DESCRIPTION</B>
     46   The stat command will output details about the specified object URLs.
     47   It is similar to running:
     48 
     49     gsutil ls -L gs://some-bucket/some-object
     50 
     51   but is more efficient because it avoids performing bucket listings and gets
     52   the minimum necessary amount of object metadata. Moreover, because it avoids
     53   performing bucket listings (which are eventually consistent) the gsutil stat
     54   command provides a strongly consistent way to check for the existence (and
     55   read the metadata) of an object.
     56 
     57   The gsutil stat command will, however, perform bucket listings if you specify
     58   URLs using wildcards.
     59 
     60   If run with the gsutil -q option nothing will be printed, e.g.:
     61 
     62     gsutil -q stat gs://some-bucket/some-object
     63 
     64   This can be useful for writing scripts, because the exit status will be 0 for
     65   an existing object and 1 for a non-existent object.
     66 
     67   Note: Unlike the gsutil ls command, the stat command does not support
     68   operations on sub-directories. For example, if you run the command:
     69 
     70     gsutil -q stat gs://some-bucket/some-subdir/
     71 
     72   gsutil will look for information about an object called "some-subdir/" (with a
     73   trailing slash) inside the bucket "some-bucket", as opposed to operating on
     74   objects nested under gs://some-bucket/some-subdir/. Unless you actually have
     75   an object with that name, the operation will fail. However, you can use the
     76   stat command on objects within subdirectories. For example, this command will
     77   work as expected:
     78 
     79     gsutil -q stat gs://some-bucket/some-subdir/file.txt
     80 """)
     81 
     82 
     83 # TODO: Add ability to stat buckets.
     84 class StatCommand(Command):
     85   """Implementation of gsutil stat command."""
     86 
     87   # Command specification. See base class for documentation.
     88   command_spec = Command.CreateCommandSpec(
     89       'stat',
     90       command_name_aliases=[],
     91       usage_synopsis=_SYNOPSIS,
     92       min_args=1,
     93       max_args=NO_MAX,
     94       supported_sub_args='',
     95       file_url_ok=False,
     96       provider_url_ok=False,
     97       urls_start_arg=0,
     98       gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
     99       gs_default_api=ApiSelector.JSON,
    100       argparse_arguments=[
    101           CommandArgument.MakeZeroOrMoreCloudURLsArgument()
    102       ]
    103   )
    104   # Help specification. See help_provider.py for documentation.
    105   help_spec = Command.HelpSpec(
    106       help_name='stat',
    107       help_name_aliases=[],
    108       help_type='command_help',
    109       help_one_line_summary='Display object status',
    110       help_text=_DETAILED_HELP_TEXT,
    111       subcommand_help_text={},
    112   )
    113 
    114   def RunCommand(self):
    115     """Command entry point for stat command."""
    116     # List of fields we'll print for stat objects.
    117     stat_fields = ['updated', 'cacheControl', 'contentDisposition',
    118                    'contentEncoding', 'contentLanguage', 'size', 'contentType',
    119                    'componentCount', 'metadata', 'crc32c', 'md5Hash', 'etag',
    120                    'generation', 'metageneration']
    121     found_nonmatching_arg = False
    122     for url_str in self.args:
    123       arg_matches = 0
    124       url = StorageUrlFromString(url_str)
    125       if not url.IsObject():
    126         raise CommandException('The stat command only works with object URLs')
    127       try:
    128         if ContainsWildcard(url_str):
    129           blr_iter = self.WildcardIterator(url_str).IterObjects(
    130               bucket_listing_fields=stat_fields)
    131         else:
    132           single_obj = self.gsutil_api.GetObjectMetadata(
    133               url.bucket_name, url.object_name, generation=url.generation,
    134               provider=url.scheme, fields=stat_fields)
    135           blr_iter = [BucketListingObject(url, root_object=single_obj)]
    136         for blr in blr_iter:
    137           if blr.IsObject():
    138             arg_matches += 1
    139             if logging.getLogger().isEnabledFor(logging.INFO):
    140               PrintFullInfoAboutObject(blr, incl_acl=False)
    141       except AccessDeniedException:
    142         sys.stderr.write('You aren\'t authorized to read %s - skipping' %
    143                          url_str)
    144       except InvalidUrlError:
    145         raise
    146       except NotFoundException:
    147         pass
    148       if not arg_matches:
    149         sys.stderr.write('No URLs matched %s' % url_str)
    150         found_nonmatching_arg = True
    151     if found_nonmatching_arg:
    152       return 1
    153     return 0
    154