jeepyb/gerritx/cmd/manage_projects.py

340 lines
12 KiB
Python

#! /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 <openstack-infra@lists.openstack.org>'"
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 " \
"<openstack-infra@lists.openstack.org>'"
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'])