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

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

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

Powered by Google App Engine
This is Rietveld 408576698