Home | History | Annotate | Download | only in commands
      1 # -*- coding: utf-8 -*-
      2 # Copyright 2011 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 gsutil version command."""
     16 
     17 from __future__ import absolute_import
     18 
     19 from hashlib import md5
     20 import os
     21 import platform
     22 import re
     23 import sys
     24 
     25 import boto
     26 import crcmod
     27 import gslib
     28 from gslib.command import Command
     29 from gslib.util import CheckMultiprocessingAvailableAndInit
     30 from gslib.util import GetConfigFilePath
     31 from gslib.util import UsingCrcmodExtension
     32 
     33 
     34 _SYNOPSIS = """
     35   gsutil version
     36 """
     37 
     38 _DETAILED_HELP_TEXT = ("""
     39 <B>SYNOPSIS</B>
     40 """ + _SYNOPSIS + """
     41 
     42 
     43 <B>DESCRIPTION</B>
     44   Prints information about the version of gsutil.
     45 
     46 <B>OPTIONS</B>
     47   -l          Prints additional information, such as the version of Python
     48               being used, the version of the Boto library, a checksum of the
     49               code, the path to gsutil, and the path to gsutil's configuration
     50               file.
     51 """)
     52 
     53 
     54 class VersionCommand(Command):
     55   """Implementation of gsutil version command."""
     56 
     57   # Command specification. See base class for documentation.
     58   command_spec = Command.CreateCommandSpec(
     59       'version',
     60       command_name_aliases=['ver'],
     61       usage_synopsis=_SYNOPSIS,
     62       min_args=0,
     63       max_args=0,
     64       supported_sub_args='l',
     65       file_url_ok=False,
     66       provider_url_ok=False,
     67       urls_start_arg=0,
     68   )
     69   # Help specification. See help_provider.py for documentation.
     70   help_spec = Command.HelpSpec(
     71       help_name='version',
     72       help_name_aliases=['ver'],
     73       help_type='command_help',
     74       help_one_line_summary='Print version info about gsutil',
     75       help_text=_DETAILED_HELP_TEXT,
     76       subcommand_help_text={},
     77   )
     78 
     79   def RunCommand(self):
     80     """Command entry point for the version command."""
     81     long_form = False
     82     if self.sub_opts:
     83       for o, _ in self.sub_opts:
     84         if o == '-l':
     85           long_form = True
     86 
     87     config_path = GetConfigFilePath()
     88 
     89     shipped_checksum = gslib.CHECKSUM
     90     try:
     91       cur_checksum = self._ComputeCodeChecksum()
     92     except IOError:
     93       cur_checksum = 'MISSING FILES'
     94     if shipped_checksum == cur_checksum:
     95       checksum_ok_str = 'OK'
     96     else:
     97       checksum_ok_str = '!= %s' % shipped_checksum
     98 
     99     sys.stdout.write('gsutil version: %s\n' % gslib.VERSION)
    100 
    101     if long_form:
    102 
    103       long_form_output = (
    104           'checksum: {checksum} ({checksum_ok})\n'
    105           'boto version: {boto_version}\n'
    106           'python version: {python_version}\n'
    107           'OS: {os_version}\n'
    108           'multiprocessing available: {multiprocessing_available}\n'
    109           'using cloud sdk: {cloud_sdk}\n'
    110           'config path: {config_path}\n'
    111           'gsutil path: {gsutil_path}\n'
    112           'compiled crcmod: {compiled_crcmod}\n'
    113           'installed via package manager: {is_package_install}\n'
    114           'editable install: {is_editable_install}\n'
    115           )
    116 
    117       sys.stdout.write(long_form_output.format(
    118           checksum=cur_checksum,
    119           checksum_ok=checksum_ok_str,
    120           boto_version=boto.__version__,
    121           python_version=sys.version.replace('\n', ''),
    122           os_version='%s %s' % (platform.system(), platform.release()),
    123           multiprocessing_available=(
    124               CheckMultiprocessingAvailableAndInit().is_available),
    125           cloud_sdk=(os.environ.get('CLOUDSDK_WRAPPER') == '1'),
    126           config_path=config_path,
    127           gsutil_path=gslib.GSUTIL_PATH,
    128           compiled_crcmod=UsingCrcmodExtension(crcmod),
    129           is_package_install=gslib.IS_PACKAGE_INSTALL,
    130           is_editable_install=gslib.IS_EDITABLE_INSTALL,
    131           ))
    132 
    133     return 0
    134 
    135   def _ComputeCodeChecksum(self):
    136     """Computes a checksum of gsutil code.
    137 
    138     This checksum can be used to determine if users locally modified
    139     gsutil when requesting support. (It's fine for users to make local mods,
    140     but when users ask for support we ask them to run a stock version of
    141     gsutil so we can reduce possible variables.)
    142 
    143     Returns:
    144       MD5 checksum of gsutil code.
    145     """
    146     if gslib.IS_PACKAGE_INSTALL:
    147       return 'PACKAGED_GSUTIL_INSTALLS_DO_NOT_HAVE_CHECKSUMS'
    148     m = md5()
    149     # Checksum gsutil and all .py files under gslib directory.
    150     files_to_checksum = [gslib.GSUTIL_PATH]
    151     for root, _, files in os.walk(gslib.GSLIB_DIR):
    152       for filepath in files:
    153         if filepath.endswith('.py'):
    154           files_to_checksum.append(os.path.join(root, filepath))
    155     # Sort to ensure consistent checksum build, no matter how os.walk
    156     # orders the list.
    157     for filepath in sorted(files_to_checksum):
    158       f = open(filepath, 'r')
    159       content = f.read()
    160       content = re.sub(r'(\r\n|\r|\n)', '\n', content)
    161       m.update(content)
    162       f.close()
    163     return m.hexdigest()
    164