Exemplo n.º 1
0
def run(masters, restart_time, reviewers, bug, force, no_commit,
        desired_state):
  """Restart all the masters in the list of masters.

  Schedules the restart for restart_time.

  Args:
    masters - a list(str) of masters to restart
    restart_time - a datetime in UTC of when to restart them. If None, restart
                   at a predefined "end of day".
    reviewers - a list(str) of reviewers for the CL (may be empty)
    bug - an integer bug number to include in the review or None
    force - a bool which causes commit not to prompt if true
    no_commit - doesn't set the CQ bit on upload
    desired_state - nominally 'running', picks which desired_state
                    to put the buildbot in
  """
  masters = [get_restart_spec(m, restart_time) for m in sorted(set(masters))]

  # Step 1: Acquire a clean master state checkout.
  # This repo is too small to consider caching.
  with get_master_state_checkout() as master_state_dir:
    master_state_json = os.path.join(
        master_state_dir, 'desired_master_state.json')

    # Step 2: make modifications to the master state json.
    LOGGER.info('Reading %s' % master_state_json)
    with open(master_state_json, 'r') as f:
      desired_master_state = json.load(f)
    LOGGER.info('Loaded')

    # Validate the current master state file.
    try:
      desired_state_parser.validate_desired_master_state(desired_master_state)
    except desired_state_parser.InvalidDesiredMasterState:
      LOGGER.exception("Failed to validate current master state JSON.")
      return 1

    master_states = desired_master_state.get('master_states', {})
    entries = 0
    for master in masters:
      if master.desired_state_name not in master_states:
        msg = '%s not found in master state' % master.desired_state_name
        LOGGER.error(msg)
        raise MasterNotFoundException(msg)

      master_states.setdefault(master.desired_state_name, []).append({
          'desired_state': desired_state,
          'transition_time_utc': zulu.to_zulu_string(master.restart_time),
      })
      entries += 1

    LOGGER.info('Writing back to JSON file, %d new entries' % (entries,))
    desired_state_parser.write_master_state(
        desired_master_state, master_state_json)

    # Step 3: Send the patch to Rietveld and commit it via the CQ.
    LOGGER.info('Committing back into repository')
    commit(master_state_dir, masters, reviewers, bug, force, no_commit,
           desired_state)
Exemplo n.º 2
0
 def testTZAwareDTZuluString(self):
   # If you're confused why GMT+8 is -08:00, see
   # http://askubuntu.com/questions/519550/
   # why-is-the-8-timezone-called-gmt-8-in-the-filesystem
   dt = datetime.datetime(2015, 06, 11, 10, 17, 26, 123,
                          tzinfo=timezone('Etc/GMT+8'))
   timestring = '2015-06-11T18:17:26.000123Z'
   self.assertEqual(zulu.to_zulu_string(dt), timestring)
Exemplo n.º 3
0
def run(masters, masters_regex, restart_time, rolling, reviewers, bug, force,
        no_commit, desired_state, reason):
    """Schedules a restart of each master in <masters> and <masters_regex>.

  Args:
    masters - a list(str) of masters to restart
    masters_regex - a regex string of master names to restart
    restart_time - a datetime in UTC of when to restart them. If None, restart
                   at a predefined "end of day".
    rolling - delay (in mins) to add between restart time of each master
    reviewers - a list(str) of reviewers for the CL (may be empty)
    bug - an integer bug number to include in the review or None
    force - a bool which causes commit not to prompt if true
    no_commit - doesn't set the CQ bit on upload
    desired_state - nominally 'running', picks which desired_state
                    to put the buildbot in
    reason - a short message saying why the master is being restarted
  """

    # Step 1: Acquire a clean master state checkout.
    # This repo is too small to consider caching.
    with get_master_state_checkout() as master_state_dir:
        # Step 1.5: make sure email of committer is @google.com.
        if not _configure_git_name_and_email(master_state_dir):
            return 0

        # Step 2: Modify the master state json file.
        master_state_json = os.path.join(master_state_dir,
                                         'desired_master_state.json')

        LOGGER.info('Reading %s' % master_state_json)
        with open(master_state_json, 'r') as f:
            desired_master_state = json.load(f)
        LOGGER.info('Loaded')

        # Validate the current master state file.
        try:
            desired_state_parser.validate_desired_master_state(
                desired_master_state)
        except desired_state_parser.InvalidDesiredMasterState:
            LOGGER.exception('Failed to validate current master state JSON.')
            return 1

        if masters_regex:
            masters.extend(
                get_master_names(desired_master_state,
                                 name_regex=masters_regex))

        masters = sorted(set(masters))

        if rolling:
            rolling_offsets = range(0, len(masters) * rolling, rolling)
            rolling_restarts = [
                restart_time + datetime.timedelta(minutes=offset)
                for offset in rolling_offsets
            ]
            masters = [
                get_restart_spec(master, time)
                for master, time in zip(masters, rolling_restarts)
            ]
        else:
            masters = [get_restart_spec(m, restart_time) for m in masters]

        # <masters> is now a list of <RestartSpec>s

        reason = reason.strip()
        if not reason:
            default_reason = ''
            if bug:
                default_reason = 'Restart for https://crbug.com/%s' % bug
            prompt = 'Please provide a reason for this restart'
            if default_reason:
                prompt += ' [%s]: ' % default_reason
            else:
                prompt += ': '
            reason = raw_input(prompt).strip()
            if not reason:
                if default_reason:
                    reason = default_reason
                else:
                    print 'No reason provided, exiting'
                    return 0

        # Update desired_master_state according to list of RestartSpecs in <masters>
        master_states = desired_master_state.get('master_states', {})
        entries = 0
        for master in masters:
            if master.desired_state_name not in master_states:
                msg = '%s not found in master state' % master.desired_state_name
                LOGGER.error(msg)
                raise MasterNotFoundException(msg)

            master_states.setdefault(master.desired_state_name, []).append({
                'desired_state':
                desired_state,
                'transition_time_utc':
                zulu.to_zulu_string(master.restart_time),
            })
            entries += 1

        LOGGER.info('Writing back to JSON file, %d new entries' % (entries, ))
        desired_state_parser.write_master_state(
            desired_master_state,
            master_state_json,
            prune_only_masters=set(m.desired_state_name for m in masters))

        # Step 3: Send the patch to Rietveld and commit it via the CQ.
        LOGGER.info('Committing back into repository')
        commit(master_state_dir, masters, reviewers, bug, force, no_commit,
               desired_state, reason)
Exemplo n.º 4
0
def commit(target, specs, reviewers, bug, force, no_commit, desired_state,
           reason):
    """Commits the local CL via the CQ."""
    if desired_state == 'running':
        action = 'Restarting'
    else:
        action = desired_state.title() + 'ing'

    desc = '%(action)s master%(plural)s %(names)s\n\n%(reason)s\n' % {
        'action': action,
        'plural': 's' if len(specs) > 1 else '',
        'names': ', '.join([s.name for s in specs]),
        'reason': reason,
    }
    if bug:
        desc += '\nBUG=%s' % bug
    tbr_whom = 'an owner'
    if reviewers:
        google, other = autocomplete_and_partition(reviewers)
        if other:
            print
            print 'Error: not @google.com email(s) for reviewers found:'
            print '  %s' % ('\n  '.join(other))
            print 'Hint: save your fingertips - use just ldap: -r <ldap>'
            return 1

        tbr_whom = ', '.join(google)
        desc += '\nTBR=%s' % tbr_whom
    subprocess.check_call(['git', 'commit', '--all', '--message', desc],
                          cwd=target)

    print
    print 'Actions for the following masters:'
    max_name_len = max(len(s.name) for s in specs)
    for s in specs:
        delta = s.restart_time - datetime.datetime.utcnow()
        restart_time_str = zulu.to_zulu_string(s.restart_time)
        local_time = s.restart_time.replace(tzinfo=dateutil.tz.tzutc())
        local_time = local_time.astimezone(dateutil.tz.tzlocal())

        print '\t- %s %-*s in %d minutes (UTC: %s, Local: %s)' % (
            action, max_name_len, s.name, delta.total_seconds() / 60,
            restart_time_str, local_time)

    for s in specs:
        if not s.message:
            continue
        print
        print '=== %s ===' % (s.name, )
        print s.message
    print

    print 'This will upload a CL for master_manager.git, TBR %s, and ' % tbr_whom
    if no_commit:
        print 'wait for you to manually commit.'
    else:
        print 'commit the CL through the CQ.'
    print

    if not force:
        if no_commit:
            print 'Upload CL? (will not set CQ bit) [Y/n]:',
        else:
            print 'Commit? [Y/n]:',
        input_string = raw_input()
        if input_string != '' and not distutils.util.strtobool(input_string):
            print 'Aborting.'
            return

    print 'To cancel, edit desired_master_state.json in %s.' % MM_REPO
    print

    LOGGER.info('Uploading to Rietveld and CQ.')
    upload_cmd = [
        'git',
        'cl',
        'upload',
        '-m',
        desc,
        '-f',
    ]
    if not reviewers:
        upload_cmd.append('--tbr-owners')
    if not no_commit:
        upload_cmd.append('-c')
    else:
        LOGGER.info('CQ bit not set, please commit manually. (--no-commit)')
    subprocess.check_call(upload_cmd, cwd=target)
Exemplo n.º 5
0
def commit(
    target, specs, reviewers, bug, force, no_commit, desired_state):
  """Commits the local CL via the CQ."""
  if desired_state == 'running':
    action = 'Restarting'
  else:
    action = desired_state.title() + 'ing'

  desc = '%s master(s) %s\n' % (
      action, ', '.join([s.name for s in specs]))
  if bug:
    desc += '\nBUG=%s' % bug
  if reviewers:
    desc += '\nTBR=%s' % ', '.join(reviewers)
  subprocess.check_call(
      ['git', 'commit', '--all', '--message', desc], cwd=target)


  print
  print 'Actions for the following masters:'
  for s in specs:
    delta = s.restart_time - datetime.datetime.utcnow()
    restart_time_str = zulu.to_zulu_string(s.restart_time)

    print '\t- %s %s in %d minutes (%s)' % (
      action, s.name, delta.total_seconds() / 60, restart_time_str)

  for s in specs:
    if not s.message:
      continue
    print
    print '=== %s ===' % (s.name,)
    print s.message
  print

  print "This will upload a CL for master_manager.git, TBR an owner, and "
  if no_commit:
    print "wait for you to manually commit."
  else:
    print "commit the CL through the CQ."
  print

  if not force:
    if no_commit:
      print 'Upload CL? (will not set CQ bit) [Y/n]:',
    else:
      print 'Commit? [Y/n]:',
    input_string = raw_input()
    if input_string != '' and not distutils.util.strtobool(input_string):
      print 'Aborting.'
      return

  print 'To cancel, edit desired_master_state.json in %s.' % MM_REPO
  print

  LOGGER.info('Uploading to Rietveld and CQ.')
  upload_cmd = [
      'git', 'cl', 'upload',
      '-m', desc,
      '-t', desc, # Title becomes the message of CL. TBR and BUG must be there.
      '-f',
  ]
  if not reviewers:
    upload_cmd.append('--tbr-owners')
  if not no_commit:
    upload_cmd.append('-c')
  else:
    LOGGER.info('CQ bit not set, please commit manually. (--no-commit)')
  subprocess.check_call(upload_cmd, cwd=target)
Exemplo n.º 6
0
 def testNaiveDTZuluStringFractional(self):
   dt = datetime.datetime(2015, 06, 11, 23, 17, 26, 123)
   timestring = '2015-06-11T23:17:26.000123Z'
   self.assertEqual(zulu.to_zulu_string(dt), timestring)