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

Side by Side Diff: third_party/gsutil/gslib/commands/update.py

Issue 2280023003: depot_tools: Remove third_party/gsutil (Closed)
Patch Set: Created 4 years, 3 months 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
OLDNEW
(Empty)
1 # Copyright 2011 Google Inc. All Rights Reserved.
2 #
3 # 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 obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import os
16 import platform
17 import shutil
18 import signal
19 import sys
20 import tarfile
21 import tempfile
22
23 from boto import config
24 from gslib.command import Command
25 from gslib.command import COMMAND_NAME
26 from gslib.command import COMMAND_NAME_ALIASES
27 from gslib.command import CONFIG_REQUIRED
28 from gslib.command import FILE_URIS_OK
29 from gslib.command import MAX_ARGS
30 from gslib.command import MIN_ARGS
31 from gslib.command import PROVIDER_URIS_OK
32 from gslib.command import SUPPORTED_SUB_ARGS
33 from gslib.command import URIS_START_ARG
34 from gslib.exception import CommandException
35 from gslib.help_provider import HELP_NAME
36 from gslib.help_provider import HELP_NAME_ALIASES
37 from gslib.help_provider import HELP_ONE_LINE_SUMMARY
38 from gslib.help_provider import HELP_TEXT
39 from gslib.help_provider import HelpType
40 from gslib.help_provider import HELP_TYPE
41
42 _detailed_help_text = ("""
43 <B>SYNOPSIS</B>
44 gsutil update [-f] [uri]
45
46
47 <B>DESCRIPTION</B>
48 The gsutil update command downloads the latest gsutil release, checks its
49 version, and offers to let you update to it if it differs from the version
50 you're currently running.
51
52 Once you say "Y" to the prompt of whether to install the update, the gsutil
53 update command locates where the running copy of gsutil is installed,
54 unpacks the new version into an adjacent directory, moves the previous version
55 aside, moves the new version to where the previous version was installed,
56 and removes the moved-aside old version. Because of this, users are cautioned
57 not to store data in the gsutil directory, since that data will be lost
58 when you update gsutil. (Some users change directories into the gsutil
59 directory to run the command. We advise against doing that, for this reason.)
60
61 By default gsutil update will retrieve the new code from
62 gs://pub/gsutil.tar.gz, but you can optionally specify a URI to use
63 instead. This is primarily used for distributing pre-release versions of
64 the code to a small group of early test users.
65
66
67 <B>OPTIONS</B>
68 -f Forces the update command to offer to let you update, even if you
69 have the most current copy already. This can be useful if you have
70 a corrupted local copy.
71 """)
72
73
74 class UpdateCommand(Command):
75 """Implementation of gsutil update command."""
76
77 # Command specification (processed by parent class).
78 command_spec = {
79 # Name of command.
80 COMMAND_NAME : 'update',
81 # List of command name aliases.
82 COMMAND_NAME_ALIASES : ['refresh'],
83 # Min number of args required by this command.
84 MIN_ARGS : 0,
85 # Max number of args required by this command, or NO_MAX.
86 MAX_ARGS : 1,
87 # Getopt-style string specifying acceptable sub args.
88 SUPPORTED_SUB_ARGS : 'f',
89 # True if file URIs acceptable for this command.
90 FILE_URIS_OK : False,
91 # True if provider-only URIs acceptable for this command.
92 PROVIDER_URIS_OK : False,
93 # Index in args of first URI arg.
94 URIS_START_ARG : 0,
95 # True if must configure gsutil before running command.
96 CONFIG_REQUIRED : True,
97 }
98 help_spec = {
99 # Name of command or auxiliary help info for which this help applies.
100 HELP_NAME : 'update',
101 # List of help name aliases.
102 HELP_NAME_ALIASES : ['refresh'],
103 # Type of help:
104 HELP_TYPE : HelpType.COMMAND_HELP,
105 # One line summary of this help.
106 HELP_ONE_LINE_SUMMARY : 'Update to the latest gsutil release',
107 # The full help text.
108 HELP_TEXT : _detailed_help_text,
109 }
110
111 def _ExplainIfSudoNeeded(self, tf, dirs_to_remove):
112 """Explains what to do if sudo needed to update gsutil software.
113
114 Happens if gsutil was previously installed by a different user (typically if
115 someone originally installed in a shared file system location, using sudo).
116
117 Args:
118 tf: Opened TarFile.
119 dirs_to_remove: List of directories to remove.
120
121 Raises:
122 CommandException: if errors encountered.
123 """
124 system = platform.system()
125 # If running under Windows we don't need (or have) sudo.
126 if system.lower().startswith('windows'):
127 return
128
129 user_id = os.getuid()
130 if (os.stat(self.gsutil_bin_dir).st_uid == user_id
131 and os.stat(self.boto_lib_dir).st_uid == user_id):
132 return
133
134 # Won't fail - this command runs after main startup code that insists on
135 # having a config file.
136 config_files = ' '.join(self.config_file_list)
137 self._CleanUpUpdateCommand(tf, dirs_to_remove)
138 raise CommandException(
139 ('Since it was installed by a different user previously, you will need '
140 'to update using the following commands.\nYou will be prompted for '
141 'your password, and the install will run as "root". If you\'re unsure '
142 'what this means please ask your system administrator for help:'
143 '\n\tchmod 644 %s\n\tsudo env BOTO_CONFIG=%s gsutil update'
144 '\n\tchmod 600 %s') % (config_files, config_files, config_files),
145 informational=True)
146
147 # This list is checked during gsutil update by doing a lowercased
148 # slash-left-stripped check. For example "/Dev" would match the "dev" entry.
149 unsafe_update_dirs = [
150 'applications', 'auto', 'bin', 'boot', 'desktop', 'dev',
151 'documents and settings', 'etc', 'export', 'home', 'kernel', 'lib',
152 'lib32', 'library', 'lost+found', 'mach_kernel', 'media', 'mnt', 'net',
153 'null', 'network', 'opt', 'private', 'proc', 'program files', 'python',
154 'root', 'sbin', 'scripts', 'srv', 'sys', 'system', 'tmp', 'users', 'usr',
155 'var', 'volumes', 'win', 'win32', 'windows', 'winnt',
156 ]
157
158 def _EnsureDirsSafeForUpdate(self, dirs):
159 """Throws Exception if any of dirs is known to be unsafe for gsutil update.
160
161 This provides a fail-safe check to ensure we don't try to overwrite
162 or delete any important directories. (That shouldn't happen given the
163 way we construct tmp dirs, etc., but since the gsutil update cleanup
164 uses shutil.rmtree() it's prudent to add extra checks.)
165
166 Args:
167 dirs: List of directories to check.
168
169 Raises:
170 CommandException: If unsafe directory encountered.
171 """
172 for d in dirs:
173 if not d:
174 d = 'null'
175 if d.lstrip(os.sep).lower() in self.unsafe_update_dirs:
176 raise CommandException('EnsureDirsSafeForUpdate: encountered unsafe '
177 'directory (%s); aborting update' % d)
178
179 def _CleanUpUpdateCommand(self, tf, dirs_to_remove):
180 """Cleans up temp files etc. from running update command.
181
182 Args:
183 tf: Opened TarFile.
184 dirs_to_remove: List of directories to remove.
185
186 """
187 tf.close()
188 self._EnsureDirsSafeForUpdate(dirs_to_remove)
189 for directory in dirs_to_remove:
190 try:
191 shutil.rmtree(directory)
192 except OSError as e:
193 # Ignore errors while attempting to remove old dirs under Windows. They
194 # happen because of Windows exclusive file locking, and the update
195 # actually succeeds but just leaves the old versions around in the
196 # user's temp dir.
197 if not platform.system().lower().startswith('windows'):
198 raise
199
200 # Command entry point.
201 def RunCommand(self):
202 for cfg_var in ('is_secure', 'https_validate_certificates'):
203 if (config.has_option('Boto', cfg_var)
204 and not config.getboolean('Boto', cfg_var)):
205 raise CommandException('Your boto configuration has %s = False. '
206 'The update command\ncannot be run this way, for '
207 'security reasons.' % cfg_var)
208 dirs_to_remove = []
209 # Retrieve gsutil tarball and check if it's newer than installed code.
210 # TODO: Store this version info as metadata on the tarball object and
211 # change this command's implementation to check that metadata instead of
212 # downloading the tarball to check the version info.
213 tmp_dir = tempfile.mkdtemp()
214 dirs_to_remove.append(tmp_dir)
215 os.chdir(tmp_dir)
216 print 'Checking for software update...'
217 if len(self.args):
218 update_from_uri_str = self.args[0]
219 if not update_from_uri_str.endswith('.tar.gz'):
220 raise CommandException(
221 'The update command only works with tar.gz files.')
222 else:
223 update_from_uri_str = 'gs://pub/gsutil.tar.gz'
224 self.command_runner.RunNamedCommand('cp', [update_from_uri_str,
225 'file://gsutil.tar.gz'],
226 self.headers, self.debug)
227 # Note: tf is closed in _CleanUpUpdateCommand.
228 tf = tarfile.open('gsutil.tar.gz')
229 tf.errorlevel = 1 # So fatal tarball unpack errors raise exceptions.
230 tf.extract('./gsutil/VERSION')
231
232 ver_file = open('gsutil/VERSION', 'r')
233 try:
234 latest_version_string = ver_file.read().rstrip('\n')
235 finally:
236 ver_file.close()
237
238 force_update = False
239 if self.sub_opts:
240 for o, unused_a in self.sub_opts:
241 if o == '-f':
242 force_update = True
243 if not force_update and self.gsutil_ver == latest_version_string:
244 self._CleanUpUpdateCommand(tf, dirs_to_remove)
245 if len(self.args):
246 raise CommandException('You already have %s installed.' %
247 update_from_uri_str, informational=True)
248 else:
249 raise CommandException('You already have the latest gsutil release '
250 'installed.', informational=True)
251
252 print(('This command will update to the "%s" version of\ngsutil at %s') %
253 (latest_version_string, self.gsutil_bin_dir))
254 self._ExplainIfSudoNeeded(tf, dirs_to_remove)
255
256 answer = raw_input('Proceed? [y/N] ')
257 if not answer or answer.lower()[0] != 'y':
258 self._CleanUpUpdateCommand(tf, dirs_to_remove)
259 raise CommandException('Not running update.', informational=True)
260
261 # Ignore keyboard interrupts during the update to reduce the chance someone
262 # hitting ^C leaves gsutil in a broken state.
263 signal.signal(signal.SIGINT, signal.SIG_IGN)
264
265 # self.gsutil_bin_dir lists the path where the code should end up (like
266 # /usr/local/gsutil), which is one level down from the relative path in the
267 # tarball (since the latter creates files in ./gsutil). So, we need to
268 # extract at the parent directory level.
269 gsutil_bin_parent_dir = os.path.dirname(self.gsutil_bin_dir)
270
271 # Extract tarball to a temporary directory in a sibling to gsutil_bin_dir.
272 old_dir = tempfile.mkdtemp(dir=gsutil_bin_parent_dir)
273 new_dir = tempfile.mkdtemp(dir=gsutil_bin_parent_dir)
274 dirs_to_remove.append(old_dir)
275 dirs_to_remove.append(new_dir)
276 self._EnsureDirsSafeForUpdate(dirs_to_remove)
277 try:
278 tf.extractall(path=new_dir)
279 except Exception, e:
280 self._CleanUpUpdateCommand(tf, dirs_to_remove)
281 raise CommandException('Update failed: %s.' % e)
282
283 # For enterprise mode (shared/central) installation, users with
284 # different user/group than the installation user/group must be
285 # able to run gsutil so we need to do some permissions adjustments
286 # here. Since enterprise mode is not not supported for Windows
287 # users, we can skip this step when running on Windows, which
288 # avoids the problem that Windows has no find or xargs command.
289 system = platform.system()
290 if not system.lower().startswith('windows'):
291 # Make all files and dirs in updated area readable by other
292 # and make all directories executable by other. These steps
293 os.system('chmod -R o+r ' + new_dir)
294 os.system('find ' + new_dir + ' -type d | xargs chmod o+x')
295
296 # Make main gsutil script readable and executable by other.
297 os.system('chmod o+rx ' + os.path.join(new_dir, 'gsutil'))
298
299 # Move old installation aside and new into place.
300 os.rename(self.gsutil_bin_dir, old_dir + os.sep + 'old')
301 os.rename(new_dir + os.sep + 'gsutil', self.gsutil_bin_dir)
302 self._CleanUpUpdateCommand(tf, dirs_to_remove)
303 signal.signal(signal.SIGINT, signal.SIG_DFL)
304 print 'Update complete.'
305 return 0
OLDNEW
« no previous file with comments | « third_party/gsutil/gslib/commands/setwebcfg.py ('k') | third_party/gsutil/gslib/commands/version.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698