OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import argparse | 5 import argparse |
6 import errno | 6 import errno |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import sys | 9 import sys |
10 import time | 10 import time |
11 import urlparse | 11 import urlparse |
12 | 12 |
| 13 from infra.libs import git2 |
| 14 from infra.services.gnumbd import gnumbd |
| 15 |
| 16 |
13 LOGGER = logging.getLogger(__name__) | 17 LOGGER = logging.getLogger(__name__) |
14 | 18 |
15 from infra.services.gnumbd.inner_loop import ( | 19 DEFAULT_REPO_DIR = 'gnumbd_repos' |
16 inner_loop, DEFAULT_CONFIG_REF, DEFAULT_REPO_DIR) | |
17 | |
18 from infra.services.gnumbd.support import git, config_ref | |
19 | 20 |
20 | 21 |
21 def parse_args(args): # pragma: no cover | 22 def parse_args(args): # pragma: no cover |
22 def check_url(s): | 23 def check_url(s): |
23 parsed = urlparse.urlparse(s) | 24 parsed = urlparse.urlparse(s) |
24 if parsed.scheme not in ('https', 'git', 'file'): | 25 if parsed.scheme not in ('https', 'git', 'file'): |
25 raise argparse.ArgumentTypeError( | 26 raise argparse.ArgumentTypeError( |
26 'Repo URL must use https, git or file protocol.') | 27 'Repo URL must use https, git or file protocol.') |
27 if not parsed.path.strip('/'): | 28 if not parsed.path.strip('/'): |
28 raise argparse.ArgumentTypeError('URL is missing a path?') | 29 raise argparse.ArgumentTypeError('URL is missing a path?') |
29 return git.Repo(s) | 30 return git2.Repo(s) |
30 | 31 |
31 parser = argparse.ArgumentParser('python -m %s' % __package__) | 32 parser = argparse.ArgumentParser('python -m %s' % __package__) |
32 g = parser.add_mutually_exclusive_group() | 33 g = parser.add_mutually_exclusive_group() |
33 g.set_defaults(log_level=logging.WARN) | 34 g.set_defaults(log_level=logging.WARN) |
34 g.add_argument('--quiet', action='store_const', const=logging.ERROR, | 35 g.add_argument('--quiet', action='store_const', const=logging.ERROR, |
35 dest='log_level', help='Make the output quieter.') | 36 dest='log_level', help='Make the output quieter.') |
36 g.add_argument('--verbose', action='store_const', const=logging.INFO, | 37 g.add_argument('--verbose', action='store_const', const=logging.INFO, |
37 dest='log_level', help='Make the output louder.') | 38 dest='log_level', help='Make the output louder.') |
38 g.add_argument('--debug', action='store_const', const=logging.DEBUG, | 39 g.add_argument('--debug', action='store_const', const=logging.DEBUG, |
39 dest='log_level', help='Make the output really loud.') | 40 dest='log_level', help='Make the output really loud.') |
40 parser.add_argument('--dry_run', action='store_true', | 41 parser.add_argument('--dry_run', action='store_true', |
41 help='Do not actually push anything.') | 42 help='Do not actually push anything.') |
42 parser.add_argument('--config_ref', metavar='REF', default=DEFAULT_CONFIG_REF, | |
43 help='The config ref to use (default: %(default)s)') | |
44 parser.add_argument('--repo_dir', metavar='DIR', default=DEFAULT_REPO_DIR, | 43 parser.add_argument('--repo_dir', metavar='DIR', default=DEFAULT_REPO_DIR, |
45 help=('The directory to use for git clones ' | 44 help=('The directory to use for git clones ' |
46 '(default: %(default)s)')) | 45 '(default: %(default)s)')) |
47 parser.add_argument('repo', nargs=1, help='The url of the repo to act on.', | 46 parser.add_argument('repo', nargs=1, help='The url of the repo to act on.', |
48 type=check_url) | 47 type=check_url) |
49 opts = parser.parse_args(args) | 48 opts = parser.parse_args(args) |
50 | 49 |
51 logging.basicConfig(level=opts.log_level) | 50 logging.basicConfig(level=opts.log_level) |
52 | 51 |
53 repo = opts.repo[0] | 52 repo = opts.repo[0] |
54 repo.dry_run = opts.dry_run | 53 repo.dry_run = opts.dry_run |
55 repo.repos_dir = os.path.abspath(opts.repo_dir) | 54 repo.repos_dir = os.path.abspath(opts.repo_dir) |
56 try: | 55 try: |
57 LOGGER.info('making repo dir: %s', repo.repos_dir) | 56 LOGGER.info('making repo dir: %s', repo.repos_dir) |
58 os.makedirs(repo.repos_dir) | 57 os.makedirs(repo.repos_dir) |
59 except OSError as e: | 58 except OSError as e: |
60 if e.errno != errno.EEXIST: | 59 if e.errno != errno.EEXIST: |
61 raise | 60 raise |
62 | 61 |
63 return repo, config_ref.ConfigRef(git.Ref(repo, opts.config_ref)) | 62 return repo |
64 | 63 |
65 | 64 |
66 def main(args): # pragma: no cover | 65 def main(args): # pragma: no cover |
67 repo, cref = parse_args(args) | 66 repo = parse_args(args) |
| 67 cref = gnumbd.GnumbdConfigRef(repo) |
68 repo.reify() | 68 repo.reify() |
69 | 69 |
70 loop_count = 0 | 70 loop_count = 0 |
71 try: | 71 try: |
72 while True: | 72 while True: |
73 start = time.time() | 73 start = time.time() |
74 LOGGER.debug('Begin loop %d', loop_count) | 74 LOGGER.debug('Begin loop %d', loop_count) |
75 | 75 |
76 print | 76 print |
77 try: | 77 try: |
78 inner_loop(repo, cref) | 78 gnumbd.inner_loop(repo, cref) |
79 except KeyboardInterrupt: | 79 except KeyboardInterrupt: |
80 raise | 80 raise |
81 except Exception: | 81 except Exception: |
82 LOGGER.exception('Uncaught exception in inner_loop') | 82 LOGGER.exception('Uncaught exception in inner_loop') |
83 | 83 |
84 LOGGER.debug('End loop %d (%f sec)', loop_count, time.time() - start) | 84 LOGGER.debug('End loop %d (%f sec)', loop_count, time.time() - start) |
85 | 85 |
86 # TODO(iannucci): This timeout should be an exponential backon/off. | 86 # TODO(iannucci): This timeout should be an exponential backon/off. |
87 # Whenever we push, we should decrease the interval at 'backon_rate' | 87 # Whenever we push, we should decrease the interval at 'backon_rate' |
88 # until we hit 'min_interval'. | 88 # until we hit 'min_interval'. |
89 # Whenever we fail/NOP, we should back off at 'backoff_rate' until we | 89 # Whenever we fail/NOP, we should back off at 'backoff_rate' until we |
90 # hit 'max_interval'. | 90 # hit 'max_interval'. |
91 # | 91 # |
92 # When all is going well, this should be looping at < 1 sec. If things | 92 # When all is going well, this should be looping at < 1 sec. If things |
93 # start going sideways, we should automatically back off. | 93 # start going sideways, we should automatically back off. |
94 time.sleep(cref['interval']) | 94 time.sleep(cref['interval']) |
95 loop_count += 1 | 95 loop_count += 1 |
96 except KeyboardInterrupt: | 96 except KeyboardInterrupt: |
97 LOGGER.warn('Stopping due to KeyboardInterrupt') | 97 LOGGER.warn('Stopping due to KeyboardInterrupt') |
98 | 98 |
99 return 0 | 99 return 0 |
100 | 100 |
101 | 101 |
102 if __name__ == '__main__': | 102 if __name__ == '__main__': |
103 sys.exit(main(sys.argv[1:])) | 103 sys.exit(main(sys.argv[1:])) |
OLD | NEW |