| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 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 contextlib | 5 import contextlib |
| 6 import datetime | 6 import datetime |
| 7 import distutils.util |
| 7 import json | 8 import json |
| 8 import logging | 9 import logging |
| 9 import os | 10 import os |
| 10 import re | 11 import re |
| 11 import shutil | 12 import shutil |
| 12 import subprocess | 13 import subprocess |
| 13 import sys | 14 import sys |
| 14 import tempfile | 15 import tempfile |
| 15 | 16 |
| 16 | 17 |
| 17 from infra.libs.time_functions import zulu | 18 from infra.libs.time_functions import zulu |
| 18 | 19 |
| 19 | 20 |
| 20 LOGGER = logging.getLogger(__name__) | 21 LOGGER = logging.getLogger(__name__) |
| 21 | 22 |
| 23 MM_REPO = 'https://chrome-internal.googlesource.com/infradata/master-manager' |
| 24 |
| 22 | 25 |
| 23 class MasterNotFoundException(Exception): | 26 class MasterNotFoundException(Exception): |
| 24 pass | 27 pass |
| 25 | 28 |
| 26 | 29 |
| 27 def add_argparse_options(parser): | 30 def add_argparse_options(parser): |
| 28 parser.add_argument( | 31 parser.add_argument( |
| 29 'masters', type=str, nargs='+', | 32 'masters', type=str, nargs='+', |
| 30 help='Master(s) to restart. "master." prefix can be omitted.') | 33 help='Master(s) to restart. "master." prefix can be omitted.') |
| 31 parser.add_argument( | 34 parser.add_argument( |
| 32 '-m', '--minutes-in-future', default=15, type=int, | 35 '-m', '--minutes-in-future', default=15, type=int, |
| 33 help='how many minutes in the future to schedule the restart. ' | 36 help='how many minutes in the future to schedule the restart. ' |
| 34 'use 0 for "now." default %(default)d') | 37 'use 0 for "now." default %(default)d') |
| 35 parser.add_argument('-b', '--bug', default=None, type=str, | 38 parser.add_argument('-b', '--bug', default=None, type=str, |
| 36 help='Bug containing master restart request.') | 39 help='Bug containing master restart request.') |
| 40 parser.add_argument( |
| 41 '-f', '--force', action='store_true', |
| 42 help='don\'t ask for confirmation, just commit') |
| 37 | 43 |
| 38 | 44 |
| 39 def get_restart_time(delta): | 45 def get_restart_time(delta): |
| 40 """Returns a zulu time string of when to restart a master, now + delta.""" | 46 """Returns a zulu time string of when to restart a master, now + delta.""" |
| 41 restart_time = datetime.datetime.utcnow() + delta | 47 restart_time = datetime.datetime.utcnow() + delta |
| 42 return zulu.to_zulu_string(restart_time) | 48 return zulu.to_zulu_string(restart_time) |
| 43 | 49 |
| 44 | 50 |
| 45 @contextlib.contextmanager | 51 @contextlib.contextmanager |
| 46 def get_master_state_checkout(): | 52 def get_master_state_checkout(): |
| 47 target_dir = tempfile.mkdtemp() | 53 target_dir = tempfile.mkdtemp() |
| 48 mm_repo = 'https://chrome-internal.googlesource.com/infradata/master-manager' | |
| 49 try: | 54 try: |
| 50 LOGGER.info('Cloning %s into %s' % (mm_repo, target_dir)) | 55 LOGGER.info('Cloning %s into %s' % (MM_REPO, target_dir)) |
| 51 subprocess.call(['git', 'clone', mm_repo, target_dir]) | 56 subprocess.call(['git', 'clone', MM_REPO, target_dir]) |
| 52 LOGGER.info('done') | 57 LOGGER.info('done') |
| 53 yield target_dir | 58 yield target_dir |
| 54 finally: | 59 finally: |
| 55 shutil.rmtree(target_dir) | 60 shutil.rmtree(target_dir) |
| 56 | 61 |
| 57 | 62 |
| 58 def commit(target, masters, bug): | 63 def commit(target, masters, bug, timestring, delta, force): |
| 59 """Commits the local CL via the CQ.""" | 64 """Commits the local CL via the CQ.""" |
| 60 desc = 'Restarting master(s) %s' % ', '.join(masters) | 65 desc = 'Restarting master(s) %s' % ', '.join(masters) |
| 61 if bug: | 66 if bug: |
| 62 desc = '%s\nBUG=%s' % (desc, bug) | 67 desc = '%s\nBUG=%s' % (desc, bug) |
| 63 subprocess.check_call( | 68 subprocess.check_call( |
| 64 ['git', 'commit', '--all', '--message', desc], cwd=target) | 69 ['git', 'commit', '--all', '--message', desc], cwd=target) |
| 70 |
| 71 print |
| 72 print 'Restarting the following masters in %d minutes (%s)' % ( |
| 73 delta.total_seconds() / 60, timestring) |
| 74 for master in sorted(masters): |
| 75 print ' %s' % master |
| 76 print |
| 77 |
| 78 print "This will upload a CL for master_manager.git, TBR an owner, and " |
| 79 print "commit the CL through the CQ." |
| 80 print |
| 81 |
| 82 if not force: |
| 83 print 'Commit? [Y/n]:', |
| 84 input_string = raw_input() |
| 85 if input_string != '' and not distutils.util.strtobool(input_string): |
| 86 print 'Aborting.' |
| 87 return |
| 88 |
| 89 print 'To cancel, edit desired_master_state.json in %s.' % MM_REPO |
| 90 print |
| 91 |
| 65 LOGGER.info('Uploading to Rietveld and CQ.') | 92 LOGGER.info('Uploading to Rietveld and CQ.') |
| 66 subprocess.check_call( | 93 subprocess.check_call( |
| 67 ['git', 'cl', 'upload', '-m', desc, '-t', desc, | 94 ['git', 'cl', 'upload', '-m', desc, '-t', desc, |
| 68 '--tbr-owners', '-c', '-f'], cwd=target) | 95 '--tbr-owners', '-c', '-f'], cwd=target) |
| 69 | 96 |
| 70 | 97 |
| 71 def run(masters, delta, bug): | 98 def run(masters, delta, bug, force): |
| 72 """Restart all the masters in the list of masters. | 99 """Restart all the masters in the list of masters. |
| 73 | 100 |
| 74 Schedules the restart for now + delta. | 101 Schedules the restart for now + delta. |
| 75 """ | 102 """ |
| 76 # Step 1: Acquire a clean master state checkout. | 103 # Step 1: Acquire a clean master state checkout. |
| 77 # This repo is too small to consider caching. | 104 # This repo is too small to consider caching. |
| 78 with get_master_state_checkout() as master_state_dir: | 105 with get_master_state_checkout() as master_state_dir: |
| 79 master_state_json = os.path.join( | 106 master_state_json = os.path.join( |
| 80 master_state_dir, 'desired_master_state.json') | 107 master_state_dir, 'desired_master_state.json') |
| 81 restart_time = get_restart_time(delta) | 108 restart_time = get_restart_time(delta) |
| (...skipping 16 matching lines...) Expand all Loading... |
| 98 'desired_state': 'running', 'transition_time_utc': restart_time | 125 'desired_state': 'running', 'transition_time_utc': restart_time |
| 99 }) | 126 }) |
| 100 | 127 |
| 101 LOGGER.info('Writing back to JSON file, %d new entries' % len(master_state)) | 128 LOGGER.info('Writing back to JSON file, %d new entries' % len(master_state)) |
| 102 with open(master_state_json, 'w') as f: | 129 with open(master_state_json, 'w') as f: |
| 103 json.dump( | 130 json.dump( |
| 104 master_state, f, sort_keys=True, indent=2, separators=(',', ':')) | 131 master_state, f, sort_keys=True, indent=2, separators=(',', ':')) |
| 105 | 132 |
| 106 # Step 3: Send the patch to Rietveld and commit it via the CQ. | 133 # Step 3: Send the patch to Rietveld and commit it via the CQ. |
| 107 LOGGER.info('Committing back into repository') | 134 LOGGER.info('Committing back into repository') |
| 108 commit(master_state_dir, masters, bug) | 135 commit(master_state_dir, masters, bug, restart_time, delta, force) |
| OLD | NEW |