18d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# -*- coding: utf-8 -*-
28d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# Copyright 2011 Google Inc. All Rights Reserved.
38d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#
48d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# Licensed under the Apache License, Version 2.0 (the "License");
58d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# you may not use this file except in compliance with the License.
68d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# You may obtain a copy of the License at
78d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#
88d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#     http://www.apache.org/licenses/LICENSE-2.0
98d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#
108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# Unless required by applicable law or agreed to in writing, software
118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# distributed under the License is distributed on an "AS IS" BASIS,
128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# See the License for the specific language governing permissions and
148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# limitations under the License.
158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi"""Implementation of gsutil version command."""
168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom __future__ import absolute_import
188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom hashlib import md5
208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport os
218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport platform
228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport re
238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport sys
248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport boto
268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport crcmod
278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport gslib
288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom gslib.command import Command
29cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom gslib.util import CheckMultiprocessingAvailableAndInit
308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom gslib.util import GetConfigFilePath
318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom gslib.util import UsingCrcmodExtension
328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi_SYNOPSIS = """
358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  gsutil version
368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi"""
378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi_DETAILED_HELP_TEXT = ("""
398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi<B>SYNOPSIS</B>
408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi""" + _SYNOPSIS + """
418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi<B>DESCRIPTION</B>
448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Prints information about the version of gsutil.
458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi<B>OPTIONS</B>
478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  -l          Prints additional information, such as the version of Python
488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi              being used, the version of the Boto library, a checksum of the
498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi              code, the path to gsutil, and the path to gsutil's configuration
508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi              file.
518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi""")
528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass VersionCommand(Command):
558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Implementation of gsutil version command."""
568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  # Command specification. See base class for documentation.
588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  command_spec = Command.CreateCommandSpec(
598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      'version',
608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      command_name_aliases=['ver'],
618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      usage_synopsis=_SYNOPSIS,
628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      min_args=0,
638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      max_args=0,
648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      supported_sub_args='l',
658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      file_url_ok=False,
668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      provider_url_ok=False,
678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      urls_start_arg=0,
688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  )
698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  # Help specification. See help_provider.py for documentation.
708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  help_spec = Command.HelpSpec(
718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      help_name='version',
728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      help_name_aliases=['ver'],
738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      help_type='command_help',
748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      help_one_line_summary='Print version info about gsutil',
758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      help_text=_DETAILED_HELP_TEXT,
768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      subcommand_help_text={},
778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  )
788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def RunCommand(self):
808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Command entry point for the version command."""
818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    long_form = False
828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if self.sub_opts:
838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      for o, _ in self.sub_opts:
848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if o == '-l':
858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          long_form = True
868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    config_path = GetConfigFilePath()
888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    shipped_checksum = gslib.CHECKSUM
908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    try:
918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      cur_checksum = self._ComputeCodeChecksum()
928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    except IOError:
938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      cur_checksum = 'MISSING FILES'
948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if shipped_checksum == cur_checksum:
958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      checksum_ok_str = 'OK'
968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      checksum_ok_str = '!= %s' % shipped_checksum
988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    sys.stdout.write('gsutil version: %s\n' % gslib.VERSION)
1008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if long_form:
1028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      long_form_output = (
1048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'checksum: {checksum} ({checksum_ok})\n'
1058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'boto version: {boto_version}\n'
1068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'python version: {python_version}\n'
1078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'OS: {os_version}\n'
1088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'multiprocessing available: {multiprocessing_available}\n'
1098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'using cloud sdk: {cloud_sdk}\n'
1108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'config path: {config_path}\n'
1118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'gsutil path: {gsutil_path}\n'
1128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'compiled crcmod: {compiled_crcmod}\n'
1138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'installed via package manager: {is_package_install}\n'
1148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          'editable install: {is_editable_install}\n'
1158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          )
1168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      sys.stdout.write(long_form_output.format(
1188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          checksum=cur_checksum,
1198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          checksum_ok=checksum_ok_str,
1208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          boto_version=boto.__version__,
1218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          python_version=sys.version.replace('\n', ''),
1228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          os_version='%s %s' % (platform.system(), platform.release()),
123cef7893435aa41160dd1255c43cb8498279738ccChris Craik          multiprocessing_available=(
124cef7893435aa41160dd1255c43cb8498279738ccChris Craik              CheckMultiprocessingAvailableAndInit().is_available),
1258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          cloud_sdk=(os.environ.get('CLOUDSDK_WRAPPER') == '1'),
1268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          config_path=config_path,
1278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          gsutil_path=gslib.GSUTIL_PATH,
1288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          compiled_crcmod=UsingCrcmodExtension(crcmod),
1298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          is_package_install=gslib.IS_PACKAGE_INSTALL,
1308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          is_editable_install=gslib.IS_EDITABLE_INSTALL,
1318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          ))
1328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return 0
1348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def _ComputeCodeChecksum(self):
1368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Computes a checksum of gsutil code.
1378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    This checksum can be used to determine if users locally modified
1398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    gsutil when requesting support. (It's fine for users to make local mods,
1408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    but when users ask for support we ask them to run a stock version of
1418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    gsutil so we can reduce possible variables.)
1428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Returns:
1448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      MD5 checksum of gsutil code.
1458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
1468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if gslib.IS_PACKAGE_INSTALL:
1478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      return 'PACKAGED_GSUTIL_INSTALLS_DO_NOT_HAVE_CHECKSUMS'
1488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    m = md5()
1498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # Checksum gsutil and all .py files under gslib directory.
1508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    files_to_checksum = [gslib.GSUTIL_PATH]
1518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    for root, _, files in os.walk(gslib.GSLIB_DIR):
1528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      for filepath in files:
1538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if filepath.endswith('.py'):
1548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          files_to_checksum.append(os.path.join(root, filepath))
1558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # Sort to ensure consistent checksum build, no matter how os.walk
1568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # orders the list.
1578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    for filepath in sorted(files_to_checksum):
1588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      f = open(filepath, 'r')
1598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      content = f.read()
1608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      content = re.sub(r'(\r\n|\r|\n)', '\n', content)
1618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      m.update(content)
1628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      f.close()
1638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return m.hexdigest()
164