setup/setup.py (246 lines of code) (raw):
#!/usr/bin/env python3
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
KIBBLE_VERSION = '0.1.0' # ABI/API compat demarcation.
KIBBLE_DB_VERSION = 2 # Second database revision
import sys
if sys.version_info <= (3, 3):
print("This script requires Python 3.4 or higher")
sys.exit(-1)
import os
import getpass
import subprocess
import argparse
import shutil
import yaml
import bcrypt
import json
mappings = json.load(open("mappings.json"))
myyaml = yaml.load(open("kibble.yaml.sample"))
dopip = False
try:
from elasticsearch import Elasticsearch
from elasticsearch import VERSION as ES_VERSION
ES_MAJOR = ES_VERSION[0]
except:
dopip = True
if dopip and (getpass.getuser() != "root"):
print("It looks like you need to install some python modules first")
print("Either run this as root to do so, or run: ")
print("pip3 install elasticsearch certifi bcrypt")
sys.exit(-1)
elif dopip:
print("Before we get started, we need to install some modules")
print("Hang on!")
try:
subprocess.check_call(('pip3','install','elasticsearch', 'certifi', 'bcrypt'))
from elasticsearch import Elasticsearch
except:
print("Oh dear, looks like this failed :(")
print("Please install elasticsearch and certifi before you try again:")
print("pip install elasticsearch certifi")
sys.exit(-1)
# Arguments for non-interactive setups like docker
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument("-e", "--hostname", help="Pre-defined hostname for ElasticSearch (docker setups)")
arg_parser.add_argument("-p", "--port", help="Pre-defined port for ES (docker setups)")
arg_parser.add_argument("-d", "--dbname", help="Pre-defined Database prefix (docker setups)")
arg_parser.add_argument("-s", "--shards", help="Predefined number of ES shards (docker setups)")
arg_parser.add_argument("-r", "--replicas", help="Predefined number of replicas for ES (docker setups)")
arg_parser.add_argument("-m", "--mailhost", help="Pre-defined mail server host (docker setups)")
arg_parser.add_argument("-a", "--autoadmin", action='store_true', help="Generate generic admin account (docker setups)")
arg_parser.add_argument("-k", "--skiponexist", action='store_true', help="Skip DB creation if DBs exist (docker setups)")
args = arg_parser.parse_args()
print("Welcome to the Apache Kibble setup script!")
print("Let's start by determining some settings...")
print("")
hostname = args.hostname or ""
port = int(args.port) if args.port else 0
dbname = args.dbname or ""
mlserver = args.mailhost or ""
mldom = ""
wc = ""
genname = ""
wce = False
shards = int(args.shards) if args.shards else 0
replicas = int(args.replicas) if args.replicas else -1
while hostname == "":
hostname = input("What is the hostname of the ElasticSearch server? [localhost]: ")
if hostname == "":
print("Using default; localhost")
hostname = "localhost"
while port < 1:
try:
port = input("What port is ElasticSearch listening on? [9200]: ")
if port == "":
print("Using default; 9200")
port = 9200
port = int(port)
except ValueError:
pass
while dbname == "":
dbname = input("What would you like to call the DB index [kibble]: ")
if dbname == "":
print("Using default; kibble")
dbname = "kibble"
while mlserver == "":
mlserver = input("What is the hostname of the outgoing mailserver? [localhost:25]: ")
if mlserver == "":
print("Using default; localhost:25")
mlserver = "localhost:25"
while shards < 1:
try:
shards = input("How many shards for the ElasticSearch index? [5]:")
if shards == "":
print("Using default; 5")
shards = 5
shards = int(shards)
except ValueError:
pass
while replicas < 0:
try:
replicas = input("How many replicas for each shard? [1]: ")
if replicas == "":
print("Using default; 1")
replicas = 1
replicas = int(replicas)
except ValueError:
pass
adminName = ""
adminPass = ""
if args.autoadmin:
adminName = "admin@kibble"
adminPass = "kibbleAdmin"
while adminName == "":
adminName = input("Enter an email address for the adminstrator account: ")
while adminPass == "":
adminPass = input("Enter a password for the adminstrator account: ")
print("Okay, I got all I need, setting up Kibble...")
def createIndex():
global mappings
es = Elasticsearch([
{
'host': hostname,
'port': port,
'use_ssl': False,
'url_prefix': ''
}],
max_retries=5,
retry_on_timeout=True
)
es6 = True if int(es.info()['version']['number'].split('.')[0]) >= 6 else False
es7 = True if int(es.info()['version']['number'].split('.')[0]) >= 7 else False
if not es6:
print("New Kibble installations require ElasticSearch 6.x or newer! You appear to be running %s!" % es.info()['version']['number'])
sys.exit(-1)
# If ES >= 7, _doc is invalid and mapping should be rooted
if es7:
mappings['mappings'] = mappings['mappings']['_doc']
# Check if index already exists
if es.indices.exists(dbname+"_api"):
if args.skiponexist: # Skip this is DB exists and -k added
print("DB prefix exists, but --skiponexist used, skipping this step.")
return
print("Error: ElasticSearch DB prefix '%s' already exists!" % dbname)
sys.exit(-1)
types = [
'api',
# ci_*: CI service stats
'ci_build',
'ci_queue',
# code_* + evolution + file_history: git repo stats
'code_commit',
'code_commit_unique',
'code_modification',
'evolution',
'file_history',
# forum_*: forum stats (SO, Discourse, Askbot etc)
'forum_post',
'forum_topic',
# GitHub stats
'ghstats',
# im_*: Instant messaging stats
'im_stats',
'im_ops',
'im_msg',
'issue',
'logstats',
# email, mail*: Email statitics
'email',
'mailstats',
'mailtop',
# organisation, view, source, publish: UI Org DB
'organisation',
'view',
'publish',
'source',
# stats: Miscellaneous stats
'stats',
# social_*: Twitter, Mastodon, Facebook etc
'social_follow',
'social_followers',
'social_follower',
'social_person',
# uisession, useraccount, message: UI user DB
'uisession',
'useraccount',
'message',
# person: contributor DB
'person',
]
for t in types:
iname = "%s_%s" % (dbname, t)
print("Creating index " + iname)
settings = {
"number_of_shards" : shards,
"number_of_replicas" : replicas
}
res = es.indices.create(index = iname, body = {
"mappings" : mappings['mappings'],
"settings": settings
}
)
print("Indices created! %s " % res)
salt = bcrypt.gensalt()
pwd = bcrypt.hashpw(adminPass.encode('utf-8'), salt).decode('ascii')
print("Creating administrator account")
doc = {
'email': adminName, # Username (email)
'password': pwd, # Hashed password
'displayName': "Administrator", # Display Name
'organisations': [], # Orgs user belongs to (default is none)
'ownerships': [], # Orgs user owns (default is none)
'defaultOrganisation': None, # Default org for user
'verified': True, # Account verified via email?
'userlevel': "admin" # User level (user/admin)
}
dbdoc = {
'apiversion': KIBBLE_VERSION, # Log current API version
'dbversion': KIBBLE_DB_VERSION # Log the database revision we accept (might change!)
}
es.index(index=dbname+'_useraccount', doc_type = '_doc', id = adminName, body = doc)
es.index(index=dbname+'_api', doc_type = '_doc', id = 'current', body = dbdoc)
print("Account created!")
try:
import logging
# elasticsearch logs lots of warnings on retries/connection failure
logging.getLogger("elasticsearch").setLevel(logging.ERROR)
createIndex()
except Exception as e:
print("Index creation failed: %s" % e)
sys.exit(1)
kibble_yaml = '../api/yaml/kibble.yaml'
if os.path.exists(kibble_yaml):
print("%s already exists! Writing to %s.tmp instead" % (kibble_yaml, kibble_yaml))
kibble_yaml = kibble_yaml + ".tmp"
print("Writing Kibble config (%s)" % kibble_yaml)
m = mlserver.split(':')
if len(m) == 1:
m.append(25)
myconfig = {
'api': {
'version': KIBBLE_VERSION,
'database': KIBBLE_DB_VERSION
},
'elasticsearch': {
'host': hostname,
'port': port,
'ssl': False,
'dbname': dbname
},
'mail': {
'mailhost': m[0],
'mailport': m[1],
'sender': 'Kibble <noreply@kibble.kibble>'
},
'accounts': {
'allowSignup': True,
'verify': True
}
}
with open(kibble_yaml, "w") as f:
f.write(yaml.dump(myconfig, default_flow_style = False))
f.close()
print("All done, Kibble should...work now :)")