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

Side by Side Diff: command_wrapper/bin/command_wrapper.py

Issue 10085001: Don't buffer stdout/stderr in command_wrapper.py (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/
Patch Set: whitespace change Created 8 years, 8 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # 2 #
3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Wrapper that does auto-retry and stats logging for command invocation. 7 """Wrapper that does auto-retry and stats logging for command invocation.
8 8
9 Various command line tools in use: gsutil, curl have spurious failure. 9 Various command line tools in use: gsutil, curl have spurious failure.
10 This wrapper will track stats to an AppEngine based service to 10 This wrapper will track stats to an AppEngine based service to
11 help track down the cause of failures, as well as add retry logic. 11 help track down the cause of failures, as well as add retry logic.
12 """ 12 """
13 13
14 14
15 import optparse 15 import optparse
16 import os 16 import os
17 import platform 17 import platform
18 import socket 18 import socket
19 import subprocess 19 import subprocess
20 import sys 20 import sys
21 import threading 21 import threading
22 import time 22 import time
23 import urllib 23 import urllib
24 import uuid 24 import uuid
25 25
26 26
27 LOG_TIMEOUT = 10 27 LOG_TIMEOUT = 10
28 ON_POSIX = 'posix' in sys.builtin_module_names
28 29
29 30
30 def LogCommand(options, command_id, 31 def LogCommand(options, command_id,
31 attempt, cmd, returncode, stdout, stderr, runtime): 32 attempt, cmd, returncode, stdout, stderr, runtime):
32 """Log a command invocation and result to a central location. 33 """Log a command invocation and result to a central location.
33 34
34 Arguments: 35 Arguments:
35 options: parsed options 36 options: parsed options
36 command_id: unique id for this command (shared by all retries) 37 command_id: unique id for this command (shared by all retries)
37 attempt: which try numbered from 0 38 attempt: which try numbered from 0
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
74 def RunWithTimeout(timeout, func, *args, **kwargs): 75 def RunWithTimeout(timeout, func, *args, **kwargs):
75 wrapper = { 'result': None } 76 wrapper = { 'result': None }
76 def CallFunc(): 77 def CallFunc():
77 wrapper['result'] = func(*args, **kwargs) 78 wrapper['result'] = func(*args, **kwargs)
78 th = threading.Thread(target=CallFunc) 79 th = threading.Thread(target=CallFunc)
79 th.start() 80 th.start()
80 th.join(timeout) 81 th.join(timeout)
81 return wrapper['result'] 82 return wrapper['result']
82 83
83 84
85 def Tee(fd, string_buffer, forward_fd):
86 """Read characters from fd and both append them to a buffer and write them to
87 forward_fd."""
88 for char in iter(lambda: fd.read(1), ''):
89 string_buffer += char
90 forward_fd.write(char)
91 fd.close()
92
93
84 def main(argv): 94 def main(argv):
85 parser = optparse.OptionParser() 95 parser = optparse.OptionParser()
86 parser.add_option('-r', '--retries', dest='retries', 96 parser.add_option('-r', '--retries', dest='retries',
87 type='int', default=10, 97 type='int', default=10,
88 help='number of times to retry on failure') 98 help='number of times to retry on failure')
89 parser.add_option('-u', '--logurl', dest='logurl', 99 parser.add_option('-u', '--logurl', dest='logurl',
90 default='https://command-wrapper.appspot.com/log', 100 default='https://command-wrapper.appspot.com/log',
91 help='URL to log invocations/failures to') 101 help='URL to log invocations/failures to')
92 (options, args) = parser.parse_args(args=argv[1:]) 102 (options, args) = parser.parse_args(args=argv[1:])
93 103
(...skipping 12 matching lines...) Expand all
106 116
107 # Log that we're even starting. 117 # Log that we're even starting.
108 RunWithTimeout(LOG_TIMEOUT, LogCommand, 118 RunWithTimeout(LOG_TIMEOUT, LogCommand,
109 options, command_id, -1, cmd, -1, '', '', 0) 119 options, command_id, -1, cmd, -1, '', '', 0)
110 120
111 # Try up to a certain number of times. 121 # Try up to a certain number of times.
112 for r in range(options.retries): 122 for r in range(options.retries):
113 tm = time.time() 123 tm = time.time()
114 p = subprocess.Popen(cmd, shell=True, 124 p = subprocess.Popen(cmd, shell=True,
115 stdout=subprocess.PIPE, 125 stdout=subprocess.PIPE,
116 stderr=subprocess.PIPE) 126 stderr=subprocess.PIPE,
117 (p_stdout, p_stderr) = p.communicate() 127 close_fds=ON_POSIX)
118 sys.stdout.write(p_stdout) 128 p_stdout = ''
119 sys.stderr.write(p_stderr) 129 t_stdout = threading.Thread(target=Tee,
130 args=(p.stdout, p_stdout, sys.stdout))
131 t_stdout.start()
132
133 p_stderr = ''
134 t_stderr = threading.Thread(target=Tee,
135 args=(p.stderr, p_stderr, sys.stderr))
136 t_stderr.start()
137
138 p.wait()
139
140 t_stdout.join()
141 t_stderr.join()
142
143
120 runtime = time.time() - tm 144 runtime = time.time() - tm
121 accept = RunWithTimeout(LOG_TIMEOUT, LogCommand, 145 accept = RunWithTimeout(LOG_TIMEOUT, LogCommand,
122 options, command_id, r, cmd, 146 options, command_id, r, cmd,
123 p.returncode, p_stdout, p_stderr, runtime) 147 p.returncode, p_stdout, p_stderr, runtime)
124 if accept: 148 if accept:
125 return p.returncode 149 return p.returncode
126 if p.returncode == 0: 150 if p.returncode == 0:
127 return 0 151 return 0
128 print 'Command %s failed with retcode %d, try %d.' % ( 152 print 'Command %s failed with retcode %d, try %d.' % (
129 ' '.join(args), p.returncode, r + 1) 153 ' '.join(args), p.returncode, r + 1)
130 print 'Command %s failed %d retries, giving up.' % ( 154 print 'Command %s failed %d retries, giving up.' % (
131 ' '.join(args), options.retries) 155 ' '.join(args), options.retries)
132 156
133 return p.returncode 157 return p.returncode
134 158
135 159
136 if __name__ == '__main__': 160 if __name__ == '__main__':
137 sys.exit(main(sys.argv)) 161 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698