
The certificates are checked both by Gertty itself (for API calls) and by the launched git processes. In theory, the server could be set up to redirect to another HTTP server for Git calls (and in fact, the KDE's Gerrit instance is set up to do just that). In that case, the CA bundle file should contain PEM certificate chain of all the CAs for both Gerrit and the webserver hosting the git repositories. Change-Id: Id6af61c3710e4809c84b1edd054ab9b1959a60c3
218 lines
7.6 KiB
Python
218 lines
7.6 KiB
Python
# Copyright 2014 OpenStack Foundation
|
|
# Copyright 2014 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.
|
|
|
|
import collections
|
|
import getpass
|
|
import os
|
|
import re
|
|
try:
|
|
import ordereddict
|
|
except:
|
|
pass
|
|
import yaml
|
|
|
|
import voluptuous as v
|
|
|
|
import gertty.commentlink
|
|
import gertty.palette
|
|
import gertty.keymap
|
|
|
|
try:
|
|
OrderedDict = collections.OrderedDict
|
|
except AttributeError:
|
|
OrderedDict = ordereddict.OrderedDict
|
|
|
|
DEFAULT_CONFIG_PATH='~/.gertty.yaml'
|
|
|
|
class ConfigSchema(object):
|
|
server = {v.Required('name'): str,
|
|
v.Required('url'): str,
|
|
v.Required('username'): str,
|
|
'password': str,
|
|
'verify-ssl': bool,
|
|
'ssl-ca-path': str,
|
|
'dburi': str,
|
|
v.Required('git-root'): str,
|
|
'log-file': str,
|
|
'auth-type': str,
|
|
}
|
|
|
|
servers = [server]
|
|
|
|
text_replacement = {'text': v.Any(str,
|
|
{'color': str,
|
|
v.Required('text'): str})}
|
|
|
|
link_replacement = {'link': {v.Required('url'): str,
|
|
v.Required('text'): str}}
|
|
|
|
search_replacement = {'search': {v.Required('query'): str,
|
|
v.Required('text'): str}}
|
|
|
|
replacement = v.Any(text_replacement, link_replacement, search_replacement)
|
|
|
|
palette = {v.Required('name'): str,
|
|
v.Match('(?!name)'): [str]}
|
|
|
|
palettes = [palette]
|
|
|
|
commentlink = {v.Required('match'): str,
|
|
v.Required('replacements'): [replacement],
|
|
'test-result': str}
|
|
|
|
commentlinks = [commentlink]
|
|
|
|
dashboard = {v.Required('name'): str,
|
|
v.Required('query'): str,
|
|
v.Required('key'): str}
|
|
|
|
dashboards = [dashboard]
|
|
|
|
reviewkey_approval = {v.Required('category'): str,
|
|
v.Required('value'): int}
|
|
|
|
reviewkey = {v.Required('approvals'): [reviewkey_approval],
|
|
'submit': bool,
|
|
v.Required('key'): str}
|
|
|
|
reviewkeys = [reviewkey]
|
|
|
|
hide_comment = {v.Required('author'): str}
|
|
|
|
hide_comments = [hide_comment]
|
|
|
|
keymap = {v.Required('name'): str,
|
|
v.Match('(?!name)'): v.Any([str], str)}
|
|
|
|
keymaps = [keymap]
|
|
|
|
def getSchema(self, data):
|
|
schema = v.Schema({v.Required('servers'): self.servers,
|
|
'palettes': self.palettes,
|
|
'palette': str,
|
|
'keymaps': self.keymaps,
|
|
'keymap': str,
|
|
'commentlinks': self.commentlinks,
|
|
'dashboards': self.dashboards,
|
|
'reviewkeys': self.reviewkeys,
|
|
'change-list-query': str,
|
|
'diff-view': str,
|
|
'hide-comments': self.hide_comments,
|
|
})
|
|
return schema
|
|
|
|
class Config(object):
|
|
def __init__(self, server=None, palette='default', keymap='default',
|
|
path=DEFAULT_CONFIG_PATH):
|
|
self.path = os.path.expanduser(path)
|
|
|
|
if not os.path.exists(self.path):
|
|
self.printSample()
|
|
exit(1)
|
|
|
|
self.config = yaml.load(open(self.path))
|
|
schema = ConfigSchema().getSchema(self.config)
|
|
schema(self.config)
|
|
server = self.getServer(server)
|
|
self.server = server
|
|
url = server['url']
|
|
if not url.endswith('/'):
|
|
url += '/'
|
|
self.url = url
|
|
self.username = server['username']
|
|
self.password = server.get('password')
|
|
if self.password is None:
|
|
self.password = getpass.getpass("Password for %s (%s): "
|
|
% (self.url, self.username))
|
|
self.auth_type = server.get('auth-type', 'digest')
|
|
auth_types = ['digest', 'basic']
|
|
if self.auth_type not in auth_types:
|
|
self.auth_type = 'digest'
|
|
self.verify_ssl = server.get('verify-ssl', True)
|
|
if not self.verify_ssl:
|
|
os.environ['GIT_SSL_NO_VERIFY']='true'
|
|
self.ssl_ca_path = server.get('ssl-ca-path', None)
|
|
if self.ssl_ca_path is not None:
|
|
self.ssl_ca_path = os.path.expanduser(self.ssl_ca_path)
|
|
# Gertty itself uses the Requests library
|
|
os.environ['REQUESTS_CA_BUNDLE'] = self.ssl_ca_path
|
|
# And this is to allow Git callouts
|
|
os.environ['GIT_SSL_CAINFO'] = self.ssl_ca_path
|
|
self.git_root = os.path.expanduser(server['git-root'])
|
|
self.dburi = server.get('dburi',
|
|
'sqlite:///' + os.path.expanduser('~/.gertty.db'))
|
|
log_file = server.get('log-file', '~/.gertty.log')
|
|
self.log_file = os.path.expanduser(log_file)
|
|
|
|
self.palettes = {'default': gertty.palette.Palette({}),
|
|
'light': gertty.palette.Palette(gertty.palette.LIGHT_PALETTE),
|
|
}
|
|
for p in self.config.get('palettes', []):
|
|
if p['name'] not in self.palettes:
|
|
self.palettes[p['name']] = gertty.palette.Palette(p)
|
|
else:
|
|
self.palettes[p['name']].update(p)
|
|
self.palette = self.palettes[self.config.get('palette', palette)]
|
|
|
|
self.keymaps = {'default': gertty.keymap.KeyMap({})}
|
|
for p in self.config.get('keymaps', []):
|
|
if p['name'] not in self.keymaps:
|
|
self.keymaps[p['name']] = gertty.keymap.KeyMap(p)
|
|
else:
|
|
self.keymaps[p['name']].update(p)
|
|
self.keymap = self.keymaps[self.config.get('keymap', keymap)]
|
|
|
|
self.commentlinks = [gertty.commentlink.CommentLink(c)
|
|
for c in self.config.get('commentlinks', [])]
|
|
self.commentlinks.append(
|
|
gertty.commentlink.CommentLink(dict(
|
|
match="(?P<url>https?://\\S*)",
|
|
replacements=[
|
|
dict(link=dict(
|
|
text="{url}",
|
|
url="{url}"))])))
|
|
|
|
self.project_change_list_query = self.config.get('change-list-query', 'status:open')
|
|
|
|
self.diff_view = self.config.get('diff-view', 'side-by-side')
|
|
|
|
self.dashboards = OrderedDict()
|
|
for d in self.config.get('dashboards', []):
|
|
self.dashboards[d['key']] = d
|
|
|
|
self.reviewkeys = OrderedDict()
|
|
for k in self.config.get('reviewkeys', []):
|
|
self.reviewkeys[k['key']] = k
|
|
|
|
self.hide_comments = []
|
|
for h in self.config.get('hide-comments', []):
|
|
self.hide_comments.append(re.compile(h['author']))
|
|
|
|
def getServer(self, name=None):
|
|
for server in self.config['servers']:
|
|
if name is None or name == server['name']:
|
|
return server
|
|
return None
|
|
|
|
def printSample(self):
|
|
filename = 'share/gertty/examples'
|
|
print """Gertty requires a configuration file at ~/.gertty.yaml
|
|
|
|
Several sample configuration files were installed with Gertty and are
|
|
available in %s in the root of the installation.
|
|
|
|
For more information, please see the README.
|
|
""" % (filename,)
|