Chris Sosa | 47a7d4e | 2012-03-28 11:26:55 -0700 | [diff] [blame] | 1 | # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | """Module containing gsutil helper methods.""" |
| 6 | |
| 7 | import subprocess |
Chris Sosa | 101fd86 | 2012-06-12 17:44:53 -0700 | [diff] [blame] | 8 | import time |
Chris Sosa | 47a7d4e | 2012-03-28 11:26:55 -0700 | [diff] [blame] | 9 | |
Gilad Arnold | abb352e | 2012-09-23 01:24:27 -0700 | [diff] [blame] | 10 | |
Chris Sosa | 47a7d4e | 2012-03-28 11:26:55 -0700 | [diff] [blame] | 11 | GSUTIL_ATTEMPTS = 5 |
| 12 | |
| 13 | |
| 14 | class GSUtilError(Exception): |
| 15 | """Exception raises when we run into an error running gsutil.""" |
| 16 | pass |
| 17 | |
| 18 | |
| 19 | def GSUtilRun(cmd, err_msg): |
| 20 | """Runs a GSUTIL command up to GSUTIL_ATTEMPTS number of times. |
| 21 | |
Chris Sosa | 101fd86 | 2012-06-12 17:44:53 -0700 | [diff] [blame] | 22 | Attempts are tried with exponential backoff. |
| 23 | |
Chris Sosa | 47a7d4e | 2012-03-28 11:26:55 -0700 | [diff] [blame] | 24 | Returns: |
| 25 | stdout of the called gsutil command. |
| 26 | Raises: |
| 27 | subprocess.CalledProcessError if all attempt to run gsutil cmd fails. |
| 28 | """ |
| 29 | proc = None |
Chris Sosa | 101fd86 | 2012-06-12 17:44:53 -0700 | [diff] [blame] | 30 | sleep_timeout = 1 |
Chris Sosa | 47a7d4e | 2012-03-28 11:26:55 -0700 | [diff] [blame] | 31 | for _attempt in range(GSUTIL_ATTEMPTS): |
| 32 | # Note processes can hang when capturing from stderr. This command |
| 33 | # specifically doesn't pipe stderr. |
| 34 | proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) |
| 35 | stdout, _stderr = proc.communicate() |
| 36 | if proc.returncode == 0: |
| 37 | return stdout |
Chris Sosa | 101fd86 | 2012-06-12 17:44:53 -0700 | [diff] [blame] | 38 | |
| 39 | time.sleep(sleep_timeout) |
| 40 | sleep_timeout *= 2 |
| 41 | |
Chris Sosa | 47a7d4e | 2012-03-28 11:26:55 -0700 | [diff] [blame] | 42 | else: |
| 43 | raise GSUtilError('%s GSUTIL cmd %s failed with return code %d' % ( |
| 44 | err_msg, cmd, proc.returncode)) |
| 45 | |
| 46 | |
| 47 | def DownloadFromGS(src, dst): |
| 48 | """Downloads object from gs_url |src| to |dst|. |
| 49 | |
| 50 | Raises: |
| 51 | GSUtilError: if an error occurs during the download. |
| 52 | """ |
| 53 | cmd = 'gsutil cp %s %s' % (src, dst) |
| 54 | msg = 'Failed to download "%s".' % src |
| 55 | GSUtilRun(cmd, msg) |