#! /usr/bin/env python # Copyright (C) 2011 OpenStack, LLC. # Copyright (c) 2012 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # manage_projects.py reads a project config file called projects.yaml # It should look like: # - homepage: http://openstack.org # gerrit-host: review.openstack.org # local-git-dir: /var/lib/git # gerrit-key: /home/gerrit2/review_site/etc/ssh_host_rsa_key # has-wiki: False # has-issues: False # has-downloads: False # --- # - project: PROJECT_NAME # options: # - has-wiki # - has-issues # - has-downloads # - has-pull-requests # homepage: Some homepage that isn't http://openstack.org # description: This is a great project # remote: https://gerrit.googlesource.com/gerrit # upstream: git://github.com/bushy/beards.git # acl_config: /path/to/gerrit/project.config import ConfigParser import logging import os import re import shlex import subprocess import tempfile import yaml import github import gerritlib.gerrit logging.basicConfig(level=logging.ERROR) log = logging.getLogger("manage_projects") def run_command(cmd, status=False, env={}): cmd_list = shlex.split(str(cmd)) newenv = os.environ newenv.update(env) log.debug("Executing command: %s" % " ".join(cmd_list)) p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=newenv) (out, nothing) = p.communicate() log.debug("Return code: %s" % p.returncode) log.debug("Command said: %s" % out.strip()) if status: return (p.returncode, out.strip()) return out.strip() def run_command_status(cmd, env={}): return run_command(cmd, True, env) def git_command(repo_dir, sub_cmd, env={}): git_dir = os.path.join(repo_dir, '.git') cmd = "git --git-dir=%s --work-tree=%s %s" % (git_dir, repo_dir, sub_cmd) status, _ = run_command(cmd, True, env) return status def git_command_output(repo_dir, sub_cmd, env={}): git_dir = os.path.join(repo_dir, '.git') cmd = "git --git-dir=%s --work-tree=%s %s" % (git_dir, repo_dir, sub_cmd) status, out = run_command(cmd, True, env) return (status, out) def fetch_config(project, remote_url, repo_path, env={}): status = git_command(repo_path, "fetch %s +refs/meta/config:" "refs/remotes/gerrit-meta/config" % remote_url, env) if status != 0: print "Failed to fetch refs/meta/config for project: %s" % project return False # Because the following fails if executed more than once you should only # run fetch_config once in each repo. status = git_command(repo_path, "checkout -b config " "remotes/gerrit-meta/config") if status != 0: print "Failed to checkout config for project: %s" % project return False return True def copy_acl_config(project, repo_path, acl_config): if not os.path.exists(acl_config): return False acl_dest = os.path.join(repo_path, "project.config") status, _ = run_command("cp %s %s" % (acl_config, acl_dest), status=True) if status == 0: status = git_command(repo_path, "diff-index --quiet HEAD --") if status != 0: return True return False def push_acl_config(project, remote_url, repo_path, env={}): cmd = "commit -a -m'Update project config.' --author='Openstack Project " \ "Creator '" status = git_command(repo_path, cmd) if status != 0: print "Failed to commit config for project: %s" % project return False status, out = git_command_output(repo_path, "push %s HEAD:refs/meta/config" % remote_url, env) if status != 0: print "Failed to push config for project: %s" % project print out return False return True def _get_group_uuid(gerrit, group): query = "select group_uuid from account_groups where name = '%s'" % group data = gerrit.dbQuery(query) if data: for row in data: if row["type"] == "row": return row["columns"]["group_uuid"] return None def get_group_uuid(gerrit, group): uuid = _get_group_uuid(gerrit, group) if uuid: return uuid gerrit.createGroup(group) uuid = _get_group_uuid(gerrit, group) if uuid: return uuid return None def create_groups_file(project, gerrit, repo_path): acl_config = os.path.join(repo_path, "project.config") group_file = os.path.join(repo_path, "groups") uuids = {} for line in open(acl_config, 'r'): r = re.match(r'^\s+.*group\s+(.*)$', line) if r: group = r.group(1) if group in uuids.keys(): continue uuid = get_group_uuid(gerrit, group) if uuid: uuids[group] = uuid else: return False if uuids: with open(group_file, 'w') as fp: for group, uuid in uuids.items(): fp.write("%s\t%s\n" % (uuid, group)) status = git_command(repo_path, "add groups") if status != 0: print "Failed to add groups file for project: %s" % project return False return True def make_ssh_wrapper(gerrit_user, gerrit_key): (fd, name) = tempfile.mkstemp(text=True) os.write(fd, '#!/bin/bash\n') os.write(fd, 'ssh -i %s -l %s -o "StrictHostKeyChecking no" $@\n' % (gerrit_key, gerrit_user)) os.close(fd) os.chmod(name, 755) return dict(GIT_SSH=name) def main(): PROJECTS_YAML = os.environ.get('PROJECTS_YAML', '/home/gerrit2/projects.yaml') configs = [config for config in yaml.load_all(open(PROJECTS_YAML))] defaults = configs[0][0] default_has_issues = defaults.get('has-issues', False) default_has_downloads = defaults.get('has-downloads', False) default_has_wiki = defaults.get('has-wiki', False) LOCAL_GIT_DIR = defaults.get('local-git-dir', '/var/lib/git') GERRIT_HOST = defaults.get('gerrit-host') GERRIT_USER = defaults.get('gerrit-user') GERRIT_KEY = defaults.get('gerrit-key') GITHUB_SECURE_CONFIG = defaults.get( 'github-config', '/etc/github/github-projects.secure.config') secure_config = ConfigParser.ConfigParser() secure_config.read(GITHUB_SECURE_CONFIG) # Project creation doesn't work via oauth ghub = github.Github(secure_config.get("github", "username"), secure_config.get("github", "password")) orgs = ghub.get_user().get_orgs() orgs_dict = dict(zip([o.login.lower() for o in orgs], orgs)) gerrit = gerritlib.gerrit.Gerrit('localhost', GERRIT_USER, 29418, GERRIT_KEY) project_list = gerrit.listProjects() ssh_env = make_ssh_wrapper(GERRIT_USER, GERRIT_KEY) try: for section in configs[1]: project = section['project'] options = section.get('options', dict()) description = section.get('description', None) homepage = section.get('homepage', defaults.get('homepage', None)) upstream = section.get('upstream', None) project_git = "%s.git" % project project_dir = os.path.join(LOCAL_GIT_DIR, project_git) # Find the project's repo project_split = project.split('/', 1) if len(project_split) > 1: repo_name = project_split[1] else: repo_name = project has_issues = 'has-issues' in options or default_has_issues has_downloads = 'has-downloads' in options or default_has_downloads has_wiki = 'has-wiki' in options or default_has_wiki try: org = orgs_dict[project_split[0].lower()] except KeyError: # We do not have control of this github org ignore the project. continue try: repo = org.get_repo(repo_name) except github.GithubException: repo = org.create_repo(repo_name, homepage=homepage, has_issues=has_issues, has_downloads=has_downloads, has_wiki=has_wiki) if description: repo.edit(repo_name, description=description) if homepage: repo.edit(repo_name, homepage=homepage) repo.edit(repo_name, has_issues=has_issues, has_downloads=has_downloads, has_wiki=has_wiki) if 'gerrit' not in [team.name for team in repo.get_teams()]: teams = org.get_teams() teams_dict = dict(zip([t.name.lower() for t in teams], teams)) teams_dict['gerrit'].add_to_repos(repo) remote_url = "ssh://localhost:29418/%s" % project if project not in project_list: tmpdir = tempfile.mkdtemp() try: repo_path = os.path.join(tmpdir, 'repo') if upstream: run_command("git clone %(upstream)s %(repo_path)s" % dict(upstream=upstream, repo_path=repo_path)) else: run_command("git init %s" % repo_path) with open(os.path.join(repo_path, ".gitreview"), 'w') as gitreview: gitreview.write(""" [gerrit] host=%s port=29418 project=%s """ % (GERRIT_HOST, project_git)) git_command(repo_path, "add .gitreview") cmd = "commit -a -m'Added .gitreview' --author=" \ "'Openstack Project Creator " \ "'" git_command(repo_path, cmd) gerrit.createProject(project) if not os.path.exists(project_dir): run_command("git --bare init %s" % project_dir) run_command("chown -R gerrit2:gerrit2 %s" % project_dir) git_command(repo_path, "push --all %s" % remote_url, env=ssh_env) git_command(repo_path, "push --tags %s" % remote_url, env=ssh_env) finally: run_command("rm -fr %s" % tmpdir) if 'acl_config' in section: tmpdir = tempfile.mkdtemp() try: repo_path = os.path.join(tmpdir, 'repo') ret, _ = run_command_status("git init %s" % repo_path) if ret != 0: continue if (fetch_config(project, remote_url, repo_path, ssh_env) and copy_acl_config(project, repo_path, section['acl_config']) and create_groups_file(project, gerrit, repo_path)): push_acl_config(project, remote_url, repo_path, ssh_env) finally: run_command("rm -fr %s" % tmpdir) finally: os.unlink(ssh_env['GIT_SSH'])