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

Side by Side Diff: gslib/commands/update.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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gslib/commands/test.py ('k') | gslib/commands/version.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # -*- coding: utf-8 -*-
1 # Copyright 2011 Google Inc. All Rights Reserved. 2 # Copyright 2011 Google Inc. All Rights Reserved.
2 # 3 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License. 5 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at 6 # You may obtain a copy of the License at
6 # 7 #
7 # http://www.apache.org/licenses/LICENSE-2.0 8 # http://www.apache.org/licenses/LICENSE-2.0
8 # 9 #
9 # Unless required by applicable law or agreed to in writing, software 10 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and 13 # See the License for the specific language governing permissions and
13 # limitations under the License. 14 # limitations under the License.
15 """Implementation of update command for updating gsutil."""
16
17 from __future__ import absolute_import
14 18
15 import os 19 import os
16 import shutil 20 import shutil
17 import signal 21 import signal
22 import stat
18 import tarfile 23 import tarfile
19 import tempfile 24 import tempfile
20 import textwrap 25 import textwrap
21 26
22 import gslib 27 import gslib
23 from gslib.command import Command 28 from gslib.command import Command
24 from gslib.command import COMMAND_NAME 29 from gslib.cs_api_map import ApiSelector
25 from gslib.command import COMMAND_NAME_ALIASES
26 from gslib.command import FILE_URIS_OK
27 from gslib.command import MAX_ARGS
28 from gslib.command import MIN_ARGS
29 from gslib.command import PROVIDER_URIS_OK
30 from gslib.command import SUPPORTED_SUB_ARGS
31 from gslib.command import URIS_START_ARG
32 from gslib.exception import CommandException 30 from gslib.exception import CommandException
33 from gslib.help_provider import HELP_NAME 31 from gslib.util import CERTIFICATE_VALIDATION_ENABLED
34 from gslib.help_provider import HELP_NAME_ALIASES
35 from gslib.help_provider import HELP_ONE_LINE_SUMMARY
36 from gslib.help_provider import HELP_TEXT
37 from gslib.help_provider import HELP_TYPE
38 from gslib.help_provider import HelpType
39 from gslib.storage_uri_builder import StorageUriBuilder
40 from gslib.util import BOTO_IS_SECURE
41 from gslib.util import CompareVersions 32 from gslib.util import CompareVersions
33 from gslib.util import GetBotoConfigFileList
42 from gslib.util import GSUTIL_PUB_TARBALL 34 from gslib.util import GSUTIL_PUB_TARBALL
43 from gslib.util import IS_CYGWIN 35 from gslib.util import IS_CYGWIN
44 from gslib.util import IS_WINDOWS 36 from gslib.util import IS_WINDOWS
45 from gslib.util import LookUpGsutilVersion 37 from gslib.util import LookUpGsutilVersion
46 from gslib.util import LookUpGsutilVersion
47 from gslib.util import RELEASE_NOTES_URL 38 from gslib.util import RELEASE_NOTES_URL
48 39
49 40
50 _detailed_help_text = (""" 41 _DETAILED_HELP_TEXT = ("""
51 <B>SYNOPSIS</B> 42 <B>SYNOPSIS</B>
52 gsutil update [-f] [-n] [uri] 43 gsutil update [-f] [-n] [uri]
53 44
54 45
55 <B>DESCRIPTION</B> 46 <B>DESCRIPTION</B>
56 The gsutil update command downloads the latest gsutil release, checks its 47 The gsutil update command downloads the latest gsutil release, checks its
57 version, and offers to let you update to it if it differs from the version 48 version, and offers to let you update to it if it differs from the version
58 you're currently running. 49 you're currently running.
59 50
60 Once you say "Y" to the prompt of whether to install the update, the gsutil 51 Once you say "Y" to the prompt of whether to install the update, the gsutil
(...skipping 29 matching lines...) Expand all
90 a corrupted local copy. 81 a corrupted local copy.
91 82
92 -n Causes update command to run without prompting [Y/n] whether to 83 -n Causes update command to run without prompting [Y/n] whether to
93 continue if an update is available. 84 continue if an update is available.
94 """ % GSUTIL_PUB_TARBALL) 85 """ % GSUTIL_PUB_TARBALL)
95 86
96 87
97 class UpdateCommand(Command): 88 class UpdateCommand(Command):
98 """Implementation of gsutil update command.""" 89 """Implementation of gsutil update command."""
99 90
100 # Command specification (processed by parent class). 91 # Command specification. See base class for documentation.
101 command_spec = { 92 command_spec = Command.CreateCommandSpec(
102 # Name of command. 93 'update',
103 COMMAND_NAME: 'update', 94 command_name_aliases=['refresh'],
104 # List of command name aliases. 95 min_args=0,
105 COMMAND_NAME_ALIASES: ['refresh'], 96 max_args=1,
106 # Min number of args required by this command. 97 supported_sub_args='fn',
107 MIN_ARGS: 0, 98 file_url_ok=True,
108 # Max number of args required by this command, or NO_MAX. 99 provider_url_ok=False,
109 MAX_ARGS: 1, 100 urls_start_arg=0,
110 # Getopt-style string specifying acceptable sub args. 101 gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
111 SUPPORTED_SUB_ARGS: 'fn', 102 gs_default_api=ApiSelector.JSON,
112 # True if file URIs acceptable for this command. 103 )
113 FILE_URIS_OK: True, 104 # Help specification. See help_provider.py for documentation.
114 # True if provider-only URIs acceptable for this command. 105 help_spec = Command.HelpSpec(
115 PROVIDER_URIS_OK: False, 106 help_name='update',
116 # Index in args of first URI arg. 107 help_name_aliases=['refresh'],
117 URIS_START_ARG: 0, 108 help_type='command_help',
118 } 109 help_one_line_summary='Update to the latest gsutil release',
119 help_spec = { 110 help_text=_DETAILED_HELP_TEXT,
120 # Name of command or auxiliary help info for which this help applies. 111 subcommand_help_text={},
121 HELP_NAME: 'update', 112 )
122 # List of help name aliases.
123 HELP_NAME_ALIASES: ['refresh'],
124 # Type of help:
125 HELP_TYPE: HelpType.COMMAND_HELP,
126 # One line summary of this help.
127 HELP_ONE_LINE_SUMMARY: 'Update to the latest gsutil release',
128 # The full help text.
129 HELP_TEXT: _detailed_help_text,
130 }
131 113
132 def _DisallowUpdataIfDataInGsutilDir(self): 114 def _DisallowUpdataIfDataInGsutilDir(self):
133 """ 115 """Disallows the update command if files not in the gsutil distro are found.
134 Disallows the update command from running if files other than those 116
135 distributed with gsutil are found. This prevents users from losing data if 117 This prevents users from losing data if they are in the habit of running
136 they are in the habit of running gsutil from the gsutil directory, and 118 gsutil from the gsutil directory and leaving data in that directory.
137 leaving data in that directory.
138 119
139 This will also detect someone attempting to run gsutil update from a git 120 This will also detect someone attempting to run gsutil update from a git
140 repo, since the top-level directory will contain git files and dirs (like 121 repo, since the top-level directory will contain git files and dirs (like
141 .git) that are not distributed with gsutil. 122 .git) that are not distributed with gsutil.
142 123
143 Raises: 124 Raises:
144 CommandException: if files other than those distributed with gsutil found. 125 CommandException: if files other than those distributed with gsutil found.
145 """ 126 """
146 # Manifest includes recursive-includes of gslib and scripts. Directly add 127 # Manifest includes recursive-includes of gslib. Directly add
147 # those to the list here so we will skip them in os.listdir() loop without 128 # those to the list here so we will skip them in os.listdir() loop without
148 # having to build deeper handling of the MANIFEST file here. Also include 129 # having to build deeper handling of the MANIFEST file here. Also include
149 # 'third_party', which isn't present in manifest but gets added to the 130 # 'third_party', which isn't present in manifest but gets added to the
150 # gsutil distro by the gsutil submodule configuration; and the MANIFEST.in 131 # gsutil distro by the gsutil submodule configuration; and the MANIFEST.in
151 # and CHANGES.md files. 132 # and CHANGES.md files.
152 manifest_lines = ['gslib', 'scripts', 'third_party', 'MANIFEST.in', 133 manifest_lines = ['gslib', 'third_party', 'MANIFEST.in', 'CHANGES.md']
153 'CHANGES.md'] 134
154
155 try: 135 try:
156 with open(os.path.join(gslib.GSUTIL_DIR, 'MANIFEST.in'), 'r') as fp: 136 with open(os.path.join(gslib.GSUTIL_DIR, 'MANIFEST.in'), 'r') as fp:
157 for line in fp.readlines(): 137 for line in fp:
158 if line.startswith('include '): 138 if line.startswith('include '):
159 manifest_lines.append(line.split()[-1]) 139 manifest_lines.append(line.split()[-1])
160 except IOError: 140 except IOError:
161 self.logger.warn('MANIFEST.in not found in %s.\nSkipping user data ' 141 self.logger.warn('MANIFEST.in not found in %s.\nSkipping user data '
162 'check.\n', gslib.GSUTIL_DIR) 142 'check.\n', gslib.GSUTIL_DIR)
163 return 143 return
164 144
165 # Look just at top-level directory. We don't try to catch data dropped into 145 # Look just at top-level directory. We don't try to catch data dropped into
166 # subdirs (like gslib) because that would require deeper parsing of 146 # subdirs (like gslib) because that would require deeper parsing of
167 # MANFFEST.in, and most users who drop data into gsutil dir do so at the top 147 # MANFFEST.in, and most users who drop data into gsutil dir do so at the top
168 # level directory. 148 # level directory.
169 for file in os.listdir(gslib.GSUTIL_DIR): 149 for filename in os.listdir(gslib.GSUTIL_DIR):
170 if file[-4:] == '.pyc': 150 if filename.endswith('.pyc'):
171 # Ignore compiled code. 151 # Ignore compiled code.
172 continue 152 continue
173 if file not in manifest_lines: 153 if filename not in manifest_lines:
174 raise CommandException('\n'.join(textwrap.wrap( 154 raise CommandException('\n'.join(textwrap.wrap(
175 'A file (%s) that is not distributed with gsutil was found in ' 155 'A file (%s) that is not distributed with gsutil was found in '
176 'the gsutil directory. The update command cannot run with user ' 156 'the gsutil directory. The update command cannot run with user '
177 'data in the gsutil directory.' % 157 'data in the gsutil directory.' %
178 os.path.join(gslib.GSUTIL_DIR, file)))) 158 os.path.join(gslib.GSUTIL_DIR, filename))))
179 159
180 def _ExplainIfSudoNeeded(self, tf, dirs_to_remove): 160 def _ExplainIfSudoNeeded(self, tf, dirs_to_remove):
181 """Explains what to do if sudo needed to update gsutil software. 161 """Explains what to do if sudo needed to update gsutil software.
182 162
183 Happens if gsutil was previously installed by a different user (typically if 163 Happens if gsutil was previously installed by a different user (typically if
184 someone originally installed in a shared file system location, using sudo). 164 someone originally installed in a shared file system location, using sudo).
185 165
186 Args: 166 Args:
187 tf: Opened TarFile. 167 tf: Opened TarFile.
188 dirs_to_remove: List of directories to remove. 168 dirs_to_remove: List of directories to remove.
189 169
190 Raises: 170 Raises:
191 CommandException: if errors encountered. 171 CommandException: if errors encountered.
192 """ 172 """
193 # If running under Windows or Cygwin we don't need (or have) sudo. 173 # If running under Windows or Cygwin we don't need (or have) sudo.
194 if IS_CYGWIN or IS_WINDOWS: 174 if IS_CYGWIN or IS_WINDOWS:
195 return 175 return
196 176
197 user_id = os.getuid() 177 user_id = os.getuid()
198 if os.stat(gslib.GSUTIL_DIR).st_uid == user_id: 178 if os.stat(gslib.GSUTIL_DIR).st_uid == user_id:
199 return 179 return
200 180
201 # Won't fail - this command runs after main startup code that insists on 181 # Won't fail - this command runs after main startup code that insists on
202 # having a config file. 182 # having a config file.
203 config_files = ' '.join(self.config_file_list) 183 config_file_list = GetBotoConfigFileList()
184 config_files = ' '.join(config_file_list)
204 self._CleanUpUpdateCommand(tf, dirs_to_remove) 185 self._CleanUpUpdateCommand(tf, dirs_to_remove)
186
187 # Pick current protection of each boto config file for command that restores
188 # protection (rather than fixing at 600) to support use cases like how GCE
189 # installs a service account with an /etc/boto.cfg file protected to 644.
190 chmod_cmds = []
191 for config_file in config_file_list:
192 mode = oct(stat.S_IMODE((os.stat(config_file)[stat.ST_MODE])))
193 chmod_cmds.append('\n\tsudo chmod %s %s' % (mode, config_file))
194
205 raise CommandException('\n'.join(textwrap.wrap( 195 raise CommandException('\n'.join(textwrap.wrap(
206 'Since it was installed by a different user previously, you will need ' 196 'Since it was installed by a different user previously, you will need '
207 'to update using the following commands. You will be prompted for your ' 197 'to update using the following commands. You will be prompted for your '
208 'password, and the install will run as "root". If you\'re unsure what ' 198 'password, and the install will run as "root". If you\'re unsure what '
209 'this means please ask your system administrator for help:')) 199 'this means please ask your system administrator for help:')) + (
210 + ('\n\tchmod 644 %s\n\tsudo env BOTO_CONFIG=%s gsutil update' 200 '\n\tsudo chmod 0644 %s\n\tsudo env BOTO_CONFIG="%s" %s update'
211 '\n\tchmod 600 %s') % (config_files, config_files, config_files), 201 '%s') % (config_files, config_files, self.gsutil_path,
212 informational=True) 202 ' '.join(chmod_cmds)), informational=True)
213 203
214 # This list is checked during gsutil update by doing a lowercased 204 # This list is checked during gsutil update by doing a lowercased
215 # slash-left-stripped check. For example "/Dev" would match the "dev" entry. 205 # slash-left-stripped check. For example "/Dev" would match the "dev" entry.
216 unsafe_update_dirs = [ 206 unsafe_update_dirs = [
217 'applications', 'auto', 'bin', 'boot', 'desktop', 'dev', 207 'applications', 'auto', 'bin', 'boot', 'desktop', 'dev',
218 'documents and settings', 'etc', 'export', 'home', 'kernel', 'lib', 208 'documents and settings', 'etc', 'export', 'home', 'kernel', 'lib',
219 'lib32', 'library', 'lost+found', 'mach_kernel', 'media', 'mnt', 'net', 209 'lib32', 'library', 'lost+found', 'mach_kernel', 'media', 'mnt', 'net',
220 'null', 'network', 'opt', 'private', 'proc', 'program files', 'python', 210 'null', 'network', 'opt', 'private', 'proc', 'program files', 'python',
221 'root', 'sbin', 'scripts', 'srv', 'sys', 'system', 'tmp', 'users', 'usr', 211 'root', 'sbin', 'scripts', 'srv', 'sys', 'system', 'tmp', 'users', 'usr',
222 'var', 'volumes', 'win', 'win32', 'windows', 'winnt', 212 'var', 'volumes', 'win', 'win32', 'windows', 'winnt',
223 ] 213 ]
224 214
225 def _EnsureDirsSafeForUpdate(self, dirs): 215 def _EnsureDirsSafeForUpdate(self, dirs):
226 """Throws Exception if any of dirs is known to be unsafe for gsutil update. 216 """Raises Exception if any of dirs is known to be unsafe for gsutil update.
227 217
228 This provides a fail-safe check to ensure we don't try to overwrite 218 This provides a fail-safe check to ensure we don't try to overwrite
229 or delete any important directories. (That shouldn't happen given the 219 or delete any important directories. (That shouldn't happen given the
230 way we construct tmp dirs, etc., but since the gsutil update cleanup 220 way we construct tmp dirs, etc., but since the gsutil update cleanup
231 uses shutil.rmtree() it's prudent to add extra checks.) 221 uses shutil.rmtree() it's prudent to add extra checks.)
232 222
233 Args: 223 Args:
234 dirs: List of directories to check. 224 dirs: List of directories to check.
235 225
236 Raises: 226 Raises:
(...skipping 21 matching lines...) Expand all
258 try: 248 try:
259 shutil.rmtree(directory) 249 shutil.rmtree(directory)
260 except OSError: 250 except OSError:
261 # Ignore errors while attempting to remove old dirs under Windows. They 251 # Ignore errors while attempting to remove old dirs under Windows. They
262 # happen because of Windows exclusive file locking, and the update 252 # happen because of Windows exclusive file locking, and the update
263 # actually succeeds but just leaves the old versions around in the 253 # actually succeeds but just leaves the old versions around in the
264 # user's temp dir. 254 # user's temp dir.
265 if not IS_WINDOWS: 255 if not IS_WINDOWS:
266 raise 256 raise
267 257
268 # Command entry point.
269 def RunCommand(self): 258 def RunCommand(self):
259 """Command entry point for the update command."""
270 260
271 if gslib.IS_PACKAGE_INSTALL: 261 if gslib.IS_PACKAGE_INSTALL:
272 raise CommandException( 262 raise CommandException(
273 'Update command is only available for gsutil installed from a ' 263 'The update command is only available for gsutil installed from a '
274 'tarball. If you installed gsutil via another method, use the same ' 264 'tarball. If you installed gsutil via another method, use the same '
275 'method to update it.') 265 'method to update it.')
276 266
277 is_secure = BOTO_IS_SECURE 267 if os.environ.get('CLOUDSDK_WRAPPER') == '1':
278 if not is_secure[0]:
279 raise CommandException( 268 raise CommandException(
280 'Your boto configuration has %s = False. The update command\n' 269 'The update command is disabled for Cloud SDK installs. Please run '
281 'cannot be run this way, for security reasons.' % is_secure[1]) 270 '"gcloud components update" to update it. Note: the Cloud SDK '
271 'incorporates updates to the underlying tools approximately every 2 '
272 'weeks, so if you are attempting to update to a recently created '
273 'release / pre-release of gsutil it may not yet be available via '
274 'the Cloud SDK.')
275
276 https_validate_certificates = CERTIFICATE_VALIDATION_ENABLED
277 if not https_validate_certificates:
278 raise CommandException(
279 'Your boto configuration has https_validate_certificates = False.\n'
280 'The update command cannot be run this way, for security reasons.')
282 281
283 self._DisallowUpdataIfDataInGsutilDir() 282 self._DisallowUpdataIfDataInGsutilDir()
284 283
285 force_update = False 284 force_update = False
286 no_prompt = False 285 no_prompt = False
287 if self.sub_opts: 286 if self.sub_opts:
288 for o, unused_a in self.sub_opts: 287 for o, unused_a in self.sub_opts:
289 if o == '-f': 288 if o == '-f':
290 force_update = True 289 force_update = True
291 if o == '-n': 290 if o == '-n':
292 no_prompt = True 291 no_prompt = True
293 292
294 dirs_to_remove = [] 293 dirs_to_remove = []
295 tmp_dir = tempfile.mkdtemp() 294 tmp_dir = tempfile.mkdtemp()
296 dirs_to_remove.append(tmp_dir) 295 dirs_to_remove.append(tmp_dir)
297 os.chdir(tmp_dir) 296 os.chdir(tmp_dir)
298 297
299 if not no_prompt: 298 if not no_prompt:
300 self.logger.info('Checking for software update...') 299 self.logger.info('Checking for software update...')
301 if self.args: 300 if self.args:
302 update_from_uri_str = self.args[0] 301 update_from_uri_str = self.args[0]
303 if not update_from_uri_str.endswith('.tar.gz'): 302 if not update_from_uri_str.endswith('.tar.gz'):
304 raise CommandException( 303 raise CommandException(
305 'The update command only works with tar.gz files.') 304 'The update command only works with tar.gz files.')
306 for i, result in enumerate(self.WildcardIterator(update_from_uri_str)): 305 for i, result in enumerate(self.WildcardIterator(update_from_uri_str)):
307 if i > 0: 306 if i > 0:
308 raise CommandException( 307 raise CommandException(
309 'Invalid update URI. Must name a single .tar.gz file.') 308 'Invalid update URI. Must name a single .tar.gz file.')
310 if result.uri.names_file(): 309 storage_url = result.storage_url
310 if storage_url.IsFileUrl() and not storage_url.IsDirectory():
311 if not force_update: 311 if not force_update:
312 raise CommandException( 312 raise CommandException(
313 ('"update" command does not support "file://" URIs without the ' 313 ('"update" command does not support "file://" URIs without the '
314 '-f option.')) 314 '-f option.'))
315 elif not result.uri.names_object(): 315 elif not (storage_url.IsCloudUrl() and storage_url.IsObject()):
316 raise CommandException( 316 raise CommandException(
317 'Invalid update object URI. Must name a single .tar.gz file.') 317 'Invalid update object URI. Must name a single .tar.gz file.')
318 else: 318 else:
319 update_from_uri_str = GSUTIL_PUB_TARBALL 319 update_from_uri_str = GSUTIL_PUB_TARBALL
320 320
321 # Try to retrieve version info from tarball metadata; failing that; download 321 # Try to retrieve version info from tarball metadata; failing that; download
322 # the tarball and extract the VERSION file. The version lookup will fail 322 # the tarball and extract the VERSION file. The version lookup will fail
323 # when running the update system test, because it retrieves the tarball from 323 # when running the update system test, because it retrieves the tarball from
324 # a temp file rather than a cloud URI (files lack the version metadata). 324 # a temp file rather than a cloud URI (files lack the version metadata).
325 suri_builder = StorageUriBuilder(self.debug, self.bucket_storage_uri_class) 325 tarball_version = LookUpGsutilVersion(self.gsutil_api, update_from_uri_str)
326 tarball_version = LookUpGsutilVersion(
327 self.suri_builder.StorageUri(update_from_uri_str))
328 if tarball_version: 326 if tarball_version:
329 tf = None 327 tf = None
330 else: 328 else:
331 tf = self._FetchAndOpenGsutilTarball(update_from_uri_str) 329 tf = self._FetchAndOpenGsutilTarball(update_from_uri_str)
332 tf.extractall() 330 tf.extractall()
333 with open(os.path.join('gsutil', 'VERSION'), 'r') as ver_file: 331 with open(os.path.join('gsutil', 'VERSION'), 'r') as ver_file:
334 tarball_version = ver_file.read().strip() 332 tarball_version = ver_file.read().strip()
335 333
336 if not force_update and gslib.VERSION == tarball_version: 334 if not force_update and gslib.VERSION == tarball_version:
337 self._CleanUpUpdateCommand(tf, dirs_to_remove) 335 self._CleanUpUpdateCommand(tf, dirs_to_remove)
338 if self.args: 336 if self.args:
339 raise CommandException('You already have %s installed.' % 337 raise CommandException('You already have %s installed.' %
340 update_from_uri_str, informational=True) 338 update_from_uri_str, informational=True)
341 else: 339 else:
342 raise CommandException('You already have the latest gsutil release ' 340 raise CommandException('You already have the latest gsutil release '
343 'installed.', informational=True) 341 'installed.', informational=True)
344 342
345 if not no_prompt: 343 if not no_prompt:
346 (g, m) = CompareVersions(tarball_version, gslib.VERSION) 344 (_, major) = CompareVersions(tarball_version, gslib.VERSION)
347 if m: 345 if major:
348 print('\n'.join(textwrap.wrap( 346 print('\n'.join(textwrap.wrap(
349 'This command will update to the "%s" version of gsutil at %s. ' 347 'This command will update to the "%s" version of gsutil at %s. '
350 'NOTE: This a major new version, so it is strongly recommended ' 348 'NOTE: This a major new version, so it is strongly recommended '
351 'that you review the release note details at %s before updating to ' 349 'that you review the release note details at %s before updating to '
352 'this version, especially if you use gsutil in scripts.' 350 'this version, especially if you use gsutil in scripts.'
353 % (tarball_version, gslib.GSUTIL_DIR, RELEASE_NOTES_URL)))) 351 % (tarball_version, gslib.GSUTIL_DIR, RELEASE_NOTES_URL))))
354 else: 352 else:
355 print('This command will update to the "%s" version of\ngsutil at %s' 353 print('This command will update to the "%s" version of\ngsutil at %s'
356 % (tarball_version, gslib.GSUTIL_DIR)) 354 % (tarball_version, gslib.GSUTIL_DIR))
357 self._ExplainIfSudoNeeded(tf, dirs_to_remove) 355 self._ExplainIfSudoNeeded(tf, dirs_to_remove)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 self._CleanUpUpdateCommand(tf, dirs_to_remove) 388 self._CleanUpUpdateCommand(tf, dirs_to_remove)
391 raise CommandException('Update failed: %s.' % e) 389 raise CommandException('Update failed: %s.' % e)
392 390
393 # For enterprise mode (shared/central) installation, users with 391 # For enterprise mode (shared/central) installation, users with
394 # different user/group than the installation user/group must be 392 # different user/group than the installation user/group must be
395 # able to run gsutil so we need to do some permissions adjustments 393 # able to run gsutil so we need to do some permissions adjustments
396 # here. Since enterprise mode is not not supported for Windows 394 # here. Since enterprise mode is not not supported for Windows
397 # users, we can skip this step when running on Windows, which 395 # users, we can skip this step when running on Windows, which
398 # avoids the problem that Windows has no find or xargs command. 396 # avoids the problem that Windows has no find or xargs command.
399 if not IS_WINDOWS: 397 if not IS_WINDOWS:
400 # Make all files and dirs in updated area readable by other 398 # Make all files and dirs in updated area owner-RW and world-R, and make
401 # and make all directories executable by other. 399 # all directories owner-RWX and world-RX.
402 os.system('chmod -R o+r ' + new_dir) 400 for dirname, subdirs, filenames in os.walk(new_dir):
403 os.system('find ' + new_dir + ' -type d | xargs chmod o+x') 401 for filename in filenames:
402 fd = os.open(os.path.join(dirname, filename), os.O_RDONLY)
403 os.fchmod(fd, stat.S_IWRITE | stat.S_IRUSR |
404 stat.S_IRGRP | stat.S_IROTH)
405 os.close(fd)
406 for subdir in subdirs:
407 fd = os.open(os.path.join(dirname, subdir), os.O_RDONLY)
408 os.fchmod(fd, stat.S_IRWXU | stat.S_IXGRP | stat.S_IXOTH |
409 stat.S_IRGRP | stat.S_IROTH)
410 os.close(fd)
404 411
405 # Make main gsutil script readable and executable by other. 412 # Make main gsutil script owner-RWX and world-RX.
406 os.system('chmod o+rx ' + os.path.join(new_dir, 'gsutil')) 413 fd = os.open(os.path.join(new_dir, 'gsutil', 'gsutil'), os.O_RDONLY)
414 os.fchmod(fd, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
415 stat.S_IROTH | stat.S_IXOTH)
416 os.close(fd)
407 417
408 # Move old installation aside and new into place. 418 # Move old installation aside and new into place.
409 os.rename(gslib.GSUTIL_DIR, os.path.join(old_dir, 'old')) 419 os.rename(gslib.GSUTIL_DIR, os.path.join(old_dir, 'old'))
410 os.rename(os.path.join(new_dir, 'gsutil'), gslib.GSUTIL_DIR) 420 os.rename(os.path.join(new_dir, 'gsutil'), gslib.GSUTIL_DIR)
411 self._CleanUpUpdateCommand(tf, dirs_to_remove) 421 self._CleanUpUpdateCommand(tf, dirs_to_remove)
412 signal.signal(signal.SIGINT, signal.SIG_DFL) 422 signal.signal(signal.SIGINT, signal.SIG_DFL)
413 self.logger.info('Update complete.') 423 self.logger.info('Update complete.')
414 return 0 424 return 0
415 425
416 def _FetchAndOpenGsutilTarball(self, update_from_uri_str): 426 def _FetchAndOpenGsutilTarball(self, update_from_uri_str):
417 self.command_runner.RunNamedCommand( 427 self.command_runner.RunNamedCommand(
418 'cp', [update_from_uri_str, 'file://gsutil.tar.gz'], self.headers, 428 'cp', [update_from_uri_str, 'file://gsutil.tar.gz'], self.headers,
419 self.debug, skip_update_check=True) 429 self.debug, skip_update_check=True)
420 # Note: tf is closed in _CleanUpUpdateCommand. 430 # Note: tf is closed in _CleanUpUpdateCommand.
421 tf = tarfile.open('gsutil.tar.gz') 431 tf = tarfile.open('gsutil.tar.gz')
422 tf.errorlevel = 1 # So fatal tarball unpack errors raise exceptions. 432 tf.errorlevel = 1 # So fatal tarball unpack errors raise exceptions.
423 return tf 433 return tf
OLDNEW
« no previous file with comments | « gslib/commands/test.py ('k') | gslib/commands/version.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698