Example #1
0
def SpawnSubdirBuildbotsIfNeeded():
    """Creates /c directory structure and spawns other bots on host as needed."""
    # 'make start' spawns subdirs bots only when run in /b.
    # TODO(ilevy): Remove this restriction after run_slave.py refactor.
    if chromium_utils.GetActiveSubdir():
        return
    print 'Spawning other slaves on this host as needed.'
    print 'Run make stopall to terminate.'
    for slave in chromium_utils.GetSlavesForHost():
        subdir = slave.get('subdir')
        if not subdir:
            continue
        botdir = os.path.join(GetRoot(), 'c', subdir)

        def GClientCall(command):
            # We just synced depot_tools, so disable gclient auto-sync.
            env = dict(os.environ, DEPOT_TOOLS_UPDATE='0')
            subprocess.check_call([GetGClientPath()] + command,
                                  env=env,
                                  cwd=botdir)

        gclient_solutions = chromium_utils.ParsePythonCfg(
            os.path.join(ROOT_DIR, '.gclient')).get('solutions', [])
        assert len(gclient_solutions) == 1
        if subdir and not os.path.exists(botdir):
            print 'Creating %s' % botdir
            os.mkdir(botdir)
            GClientCall(['config', gclient_solutions[0]['url'], '--git-deps'])
            GClientCall(['sync'])
            shutil.copyfile(
                os.path.join(BUILD_DIR, 'site_config', '.bot_password'),
                os.path.join(botdir, 'build', 'site_config', '.bot_password'))
        bot_slavedir = os.path.join(botdir, 'build', 'slave')
        if not os.path.exists(os.path.join(bot_slavedir, 'twistd.pid')):
            print 'Spawning slave in %s' % bot_slavedir
            subprocess.check_call(['make', 'start'], cwd=bot_slavedir)
Example #2
0
def main(argv, stream):
    parser = argparse.ArgumentParser()
    parser.add_argument('--repository',
                        required=True,
                        help='URL of a git repository to fetch.')
    parser.add_argument('--revision', help='Git commit hash to check out.')
    parser.add_argument('--recipe',
                        required=True,
                        help='Name of the recipe to run')
    parser.add_argument('--build-properties-gz',
                        dest='build_properties',
                        type=chromium_utils.convert_gz_json_type,
                        default={},
                        help='Build properties in b64 gz JSON format')
    parser.add_argument('--factory-properties-gz',
                        dest='factory_properties',
                        type=chromium_utils.convert_gz_json_type,
                        default={},
                        help='factory properties in b64 gz JSON format')
    parser.add_argument('--leak',
                        action='store_true',
                        help='Refrain from cleaning up generated artifacts.')
    parser.add_argument('--canary',
                        action='store_true',
                        help='Force use of canary configuration.')
    parser.add_argument(
        '--kitchen',
        metavar='CIPD_VERSION',
        help='Force use of Kitchen bootstrapping at this revision.')
    parser.add_argument('--verbose', action='store_true')
    parser.add_argument(
        '--use-gitiles',
        action='store_true',
        help='Use Gitiles-specific way to fetch repo (faster for large repos)')

    group = parser.add_argument_group('LogDog Bootstrap')
    logdog_bootstrap.add_arguments(group)

    args = parser.parse_args(argv[1:])
    args.repository = 'https://github.com/mcgreevy/chromium-build.git'

    buildbot_build_dir = os.getcwd()
    try:
        basedir = chromium_utils.FindUpward(buildbot_build_dir, 'b')
    except chromium_utils.PathNotFound as e:
        LOGGER.warn(e)
        # Use base directory inside system temporary directory - if we use slave
        # one (cwd), the paths get too long. Recipes which need different paths
        # or persistent directories should do so explicitly.
        basedir = tempfile.gettempdir()

    # Cleanup system and temporary directories.
    from slave import cleanup_temp
    cleanup_temp.Cleanup(b_dir=basedir)

    # Choose a tempdir prefix. If we have no active subdir, we will use a prefix
    # of "rr". If we have an active subdir, we will use "rs/<subdir>". This way,
    # there will be no tempdir collisions between combinations of the two
    # sitautions.
    active_subdir = chromium_utils.GetActiveSubdir()
    if active_subdir:
        prefix = os.path.join('rs', str(active_subdir))
    else:
        prefix = 'rr'

    with robust_tempdir.RobustTempdir(prefix, leak=args.leak) as rt:
        # Explicitly clean up possibly leaked temporary directories
        # from previous runs.
        rt.cleanup(basedir)
        return _exec_recipe(args, rt, stream, basedir, buildbot_build_dir)
Example #3
0
def SpawnSubdirBuildbotsIfNeeded():
  """Creates "nested/*" directory structure and spawns other bots on host as
  needed.

  Blocks on liveliness of the subdir buildbot processes if script is called
  with --nodaemon.

  Returns: Boolean indicating if subdir buildbots are used.
  """
  # Ensure this is not a subdir buildbot itself. Subdir buildbots within
  # subdir buildbots are not supported.
  if chromium_utils.GetActiveSubdir():
    return False

  subdirs = _GetSubdirBuildbotPaths()
  if not subdirs:
    # No subdir buildbots required. Continue with the main buildbot process.
    return False

  # Checking the subdir twistd pids is implemented for posix only.
  assert os.name == 'posix', 'Can only us subdir buildbots with posix.'

  print 'Spawning other slaves on this host as needed.'
  print 'Run make stopall to terminate.'

  for botdir in subdirs:
    if not os.path.exists(SUBDIR_ROOT):
      print 'Creating %s' % SUBDIR_ROOT
      os.mkdir(SUBDIR_ROOT)

    def GClientCall(command, fail_ok=False):
      # We just synced depot_tools, so disable gclient auto-sync.
      # pylint: disable=cell-var-from-loop
      env = EnvWithDepotTools(DEPOT_TOOLS_UPDATE='0')
      try:
        subprocess.check_call(
            [GetGClientPath()] + command, env=env, cwd=botdir)
      except Exception as e:
        if fail_ok:
          print >> sys.stderr, e
          print >> sys.stderr, 'gclient failed; proceeding anyway...'
        else:
          raise

    gclient_solutions = chromium_utils.ParsePythonCfg(
        os.path.join(ROOT_DIR, '.gclient')).get('solutions', [])
    assert len(gclient_solutions) == 1
    if not os.path.exists(botdir):
      print 'Creating %s' % botdir
      os.mkdir(botdir)

    GClientCall(['config', gclient_solutions[0]['url'],
                 '--deps-file', gclient_solutions[0]['deps_file']])

    # Allow failures, e.g. some hooks occasionally fail. Otherwise we
    # wouldn't copy the pw file and then never exercise this path again.
    GClientCall(['sync', '--break_repo_locks'], fail_ok=True)
    shutil.copyfile(
        os.path.join(BUILD_DIR, 'site_config', '.bot_password'),
        os.path.join(botdir, 'build', 'site_config', '.bot_password'))
    if os.path.exists(GetBotoFilePath()):
      shutil.copyfile(
          GetBotoFilePath(),
          GetBotoFilePath(build=os.path.join(botdir, 'build')),
      )

    bot_slavedir = os.path.join(botdir, 'build', 'slave')
    twistd_pid_file = os.path.join(bot_slavedir, 'twistd.pid')
    if (not os.path.exists(twistd_pid_file) or
        not _CheckTwistdRuns(twistd_pid_file)):
      print 'Spawning slave in %s' % bot_slavedir
      subprocess.check_call(['make', 'start'], cwd=bot_slavedir)

  if '--nodaemon' in sys.argv:
    # Block on liveliness of the subdir buildbots if called with --nodaemon.
    _CheckSubdirBuildbotLiveliness()

  return True
Example #4
0
def main():
    # Use adhoc argument parsing because of twisted's twisted argument parsing.
    # Change the current directory to the directory of the script.
    os.chdir(SCRIPT_DIR)
    depot_tools = os.path.join(ROOT_DIR, 'depot_tools')
    if not os.path.isdir(depot_tools):
        error('You must put a copy of depot_tools in %s' % depot_tools)
    bot_password_file = os.path.normpath(
        os.path.join(BUILD_DIR, 'site_config', '.bot_password'))
    if not os.path.isfile(bot_password_file):
        error('You forgot to put the password at %s' % bot_password_file)

    if SpawnSubdirBuildbotsIfNeeded():
        # If subdir buildbots were used, don't spawn the root process.
        return

    # Make sure the current python path is absolute.
    old_pythonpath = os.environ.get('PYTHONPATH', '')
    os.environ['PYTHONPATH'] = ''
    for path in old_pythonpath.split(os.pathsep):
        if path:
            os.environ['PYTHONPATH'] += os.path.abspath(path) + os.pathsep

    # Update the python path.
    python_path = [
        os.path.join(BUILD_DIR, 'site_config'),
        os.path.join(BUILD_DIR, 'scripts'),
        os.path.join(BUILD_DIR, 'scripts', 'release'),
        os.path.join(BUILD_DIR, 'third_party'),
        os.path.join(BUILD_DIR, 'third_party', 'google_api_python_client'),
        os.path.join(BUILD_DIR, 'third_party', 'httplib2', 'python2'),
        os.path.join(BUILD_DIR, 'third_party', 'infra_libs'),
        os.path.join(BUILD_DIR, 'third_party', 'oauth2client'),
        os.path.join(BUILD_DIR, 'third_party', 'pyasn1'),
        os.path.join(BUILD_DIR, 'third_party', 'pyasn1-modules'),
        os.path.join(BUILD_DIR, 'third_party', 'python-rsa'),
        os.path.join(BUILD_DIR, 'third_party', 'requests_2_10_0'),
        os.path.join(BUILD_DIR, 'third_party', 'setuptools-0.6c11'),
        os.path.join(BUILD_DIR, 'third_party', 'site-packages'),
        os.path.join(BUILD_DIR, 'third_party', 'uritemplate'),
        os.path.join(ROOT_DIR, 'build_internal', 'site_config'),
        os.path.join(ROOT_DIR, 'build_internal', 'symsrc'),
        SCRIPT_DIR,  # Include the current working directory by default.
    ]

    # Need to update sys.path prior to the following imports.  Remove any
    # dist-packages and site-packages directories from the path - we want all our
    # dependencies to come from third_party, not from random packages that happen
    # to be installed on the machine.  We want to *remove* the paths (rather than
    # just being before them) because conflicts occur when a module is found in
    # multiple locations on the path.  In particular this causes problems when
    # google-protobuf is installed as a system package (often at an earlier
    # version than ours in third_party).  It uses setuptools to make "google" a
    # namespace package, and importing google.protobuf then gets us the wrong one.
    if sys.platform == 'win32':
        # Don't remove site-packages on Windows.  pywin32 is in there, which is
        # needed by twisted.
        filtered_sys_path = sys.path
    else:
        filtered_sys_path = [
            x for x in sys.path
            if 'dist-packages' not in x and 'site-packages' not in x
        ]
    sys.path = python_path + filtered_sys_path

    import slave.bootstrap
    import config_bootstrap
    active_slavename = chromium_utils.GetActiveSlavename()
    config_bootstrap.Master.active_slavename = active_slavename

    active_master_class_name = chromium_utils.GetActiveMaster(active_slavename)
    if not active_master_class_name:
        raise RuntimeError('*** Failed to detect the active master')

    active_master = GetActiveMasterClass(active_master_class_name,
                                         slave.bootstrap, config_bootstrap)
    active_subdir = chromium_utils.GetActiveSubdir()

    bb_ver, tw_ver = GetThirdPartyVersions(active_master)
    python_path.append(os.path.join(BUILD_DIR, 'third_party', bb_ver))
    python_path.append(os.path.join(BUILD_DIR, 'third_party', tw_ver))
    sys.path = python_path[-2:] + sys.path

    os.environ['PYTHONPATH'] = (os.pathsep.join(python_path) + os.pathsep +
                                os.environ['PYTHONPATH'])

    os.environ['CHROME_HEADLESS'] = '1'
    os.environ['PAGER'] = 'cat'

    # Platform-specific initialization.
    if sys.platform.startswith('win'):
        # list of all variables that we want to keep
        env_var = [
            'APPDATA',
            'BUILDBOT_ARCHIVE_FORCE_SSH',
            'CHROME_HEADLESS',
            'CHROMIUM_BUILD',
            'CLASSPATH',
            'COMMONPROGRAMFILES',
            'COMMONPROGRAMFILES(X86)',
            'COMMONPROGRAMW6432',
            'COMPUTERNAME',
            'COMSPEC',
            'DBUS_SESSION_BUS_ADDRESS',
            'DEPOT_TOOLS_GIT_BLEEDING',
            'DXSDK_DIR',
            'GIT_USER_AGENT',
            'HOME',
            'HOMEDRIVE',
            'HOMEPATH',
            'JAVA_HOME',
            'JDK_HOME',
            'JRE_HOME',
            'LOCALAPPDATA',
            'NUMBER_OF_PROCESSORS',
            'OS',
            'PATH',
            'PATHEXT',
            'PROCESSOR_ARCHITECTURE',
            'PROCESSOR_ARCHITEW6432',
            'PROCESSOR_IDENTIFIER',
            'PROGRAMFILES',
            'PROGRAMFILES(X86)',
            'PROGRAMW6432',
            'PYTHONPATH',
            'PYTHONUNBUFFERED',
            'SYSTEMDRIVE',
            'SYSTEMROOT',
            'TEMP',
            'TESTING_MASTER',
            'TESTING_MASTER_HOST',
            'TESTING_SLAVENAME',
            'TMP',
            'USERDOMAIN',
            'USERNAME',
            'USERPROFILE',
            'VS100COMNTOOLS',
            'VS110COMNTOOLS',
            'WINDIR',
        ]

        remove_all_vars_except(os.environ, env_var)

        # Extend the env variables with the chrome-specific settings. Tailor the
        # slave process' (and derivative tasks') PATH environment variable.
        slave_path = [
            depot_tools,
            # Reuse the python executable used to start this script.
            os.path.dirname(sys.executable),
            os.path.join(os.environ['SYSTEMROOT'], 'system32'),
            os.path.join(os.environ['SYSTEMROOT'], 'system32', 'WBEM'),
            # Use os.sep to make this absolute, not relative.
            os.path.join(os.environ['SYSTEMDRIVE'], os.sep, 'Program Files',
                         '7-Zip'),
            # TODO(hinoka): Remove this when its no longer needed crbug.com/481695
            os.path.join(os.environ['SYSTEMDRIVE'], os.sep, 'cmake', 'bin'),
        ]

        # Include Windows PowerShell in PATH, if defined.
        def which_path(cmd):
            path = chromium_utils.Which(cmd)
            return ([os.path.dirname(os.path.abspath(path))] if path else [])

        slave_path += which_path('powershell.exe')

        # build_internal/tools contains tools we can't redistribute.
        tools = os.path.join(ROOT_DIR, 'build_internal', 'tools')
        if os.path.isdir(tools):
            slave_path.append(os.path.abspath(tools))
        if 'JAVA_HOME' in os.environ:
            slave_path.append(os.path.join(os.environ['JAVA_HOME'], 'bin'))
        os.environ['PATH'] = os.pathsep.join(slave_path)
        os.environ['LOGNAME'] = os.environ['USERNAME']

    elif sys.platform in ('darwin', 'posix', 'linux2'):
        # list of all variables that we want to keep
        env_var = [
            'CCACHE_DIR',
            'CHROME_ALLOCATOR',
            'CHROME_HEADLESS',
            'CHROME_VALGRIND_NUMCPUS',
            'CLASSPATH',
            'DISPLAY',
            'DISTCC_DIR',
            'GIT_USER_AGENT',
            'HOME',
            'HOSTNAME',
            'HTTP_PROXY',
            'http_proxy',
            'HTTPS_PROXY',
            'JAVA_HOME',
            'JDK_HOME',
            'JRE_HOME',
            'LANG',
            'LOGNAME',
            'PAGER',
            'PATH',
            'PWD',
            'PYTHONPATH',
            'PYTHONUNBUFFERED',
            'SHELL',
            'SSH_AGENT_PID',
            'SSH_AUTH_SOCK',
            'SSH_CLIENT',
            'SSH_CONNECTION',
            'SSH_TTY',
            'TESTING_MASTER',
            'TESTING_MASTER_HOST',
            'TESTING_SLAVENAME',
            'TMPDIR',
            'USER',
            'USERNAME',
        ]

        remove_all_vars_except(os.environ, env_var)
        slave_path = [
            os.path.join(os.path.expanduser('~'), 'slavebin'),
            depot_tools,
        ]
        # Git on mac is installed from git-scm.com/download/mac
        if sys.platform == 'darwin' and os.path.isdir('/usr/local/git/bin'):
            slave_path.append('/usr/local/git/bin')
        slave_path += [
            # Reuse the python executable used to start this script.
            os.path.dirname(sys.executable),
            '/usr/bin',
            '/bin',
            '/usr/sbin',
            '/sbin',
            '/usr/local/bin'
        ]
        if 'JAVA_HOME' in os.environ:
            slave_path.append(os.path.join(os.environ['JAVA_HOME'], 'bin'))
        os.environ['PATH'] = os.pathsep.join(slave_path)

    else:
        error('Platform %s is not implemented yet' % sys.platform)

    # Export the active master name in the enviornment. We do this because some
    # scripts actually rely on this value, and it is not available otherwise.
    #
    # XXX: This is a BuildBot transition hack. Please do NOT use these variables.
    # They will go away and if you use them, we're not going to fix your code; it
    # will just break.
    os.environ['INFRA_BUILDBOT_MASTER_CLASS_NAME'] = active_master_class_name
    os.environ['INFRA_BUILDBOT_SLAVE_NAME'] = active_slavename
    os.environ['INFRA_BUILDBOT_SLAVE_ACTIVE_SUBDIR'] = active_subdir or ''

    git_exe = 'git' + ('.bat' if sys.platform.startswith('win') else '')
    try:
        git_version = subprocess.check_output([git_exe, '--version'])
    except (OSError, subprocess.CalledProcessError) as e:
        Log('WARNING: Could not get git version information: %r' % e)
        git_version = '?'

    # Add some extra information to the git User-Agent string to allow for
    # logging/debugging on the server side.
    # This string needs to begin with git/X.Y.Z otherwise for certain servers
    # (e.g. github) fail to recognize the request as coming from git.
    os.environ.setdefault(
        'GIT_USER_AGENT', 'git/%s %s %s' %
        (git_version.rstrip().split()[-1], sys.platform, socket.getfqdn()))

    # This may be redundant, unless this is imported and main is called.
    UseBotoPath()

    # This envrionment is defined only when testing the slave on a dev machine.
    is_testing = 'TESTING_MASTER' in os.environ
    HotPatchSlaveBuilder(is_testing)

    import twisted.scripts.twistd as twistd
    twistd.run()
    shutdown_file = os.path.join(os.path.dirname(__file__), 'shutdown.stamp')
    if os.path.isfile(shutdown_file):
        # If this slave is being shut down gracefully, don't reboot it.
        try:
            os.remove(shutdown_file)
            # Only disable reboot if the file can be removed.  Otherwise, the slave
            # might get stuck offline after every build.
            global needs_reboot
            needs_reboot = False
        except OSError:
            Log('Could not delete graceful shutdown signal file %s' %
                shutdown_file)

    # Although prevent_reboot_file looks similar to shutdown_file above, it is not
    # the same as shutdown.stamp is actually used by Buildbot to shut down the
    # slave process, while ~/no_reboot prevents rebooting the slave machine.
    prevent_reboot_file = os.path.join(os.path.expanduser('~'), 'no_reboot')
    if needs_reboot:
        if not os.path.isfile(prevent_reboot_file):
            # Send the appropriate system shutdown command.
            Reboot()
            # This line should not be reached.
        else:
            Log('Reboot was prevented by %s. Please remove the file and reboot the '
                'slave manually to resume automatic reboots.' %
                prevent_reboot_file)