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