1b2d29daeSVladimir Kotal#!/usr/bin/env python3 2b2d29daeSVladimir Kotal 3b2d29daeSVladimir Kotal# CDDL HEADER START 4b2d29daeSVladimir Kotal# 5b2d29daeSVladimir Kotal# The contents of this file are subject to the terms of the 6b2d29daeSVladimir Kotal# Common Development and Distribution License (the "License"). 7b2d29daeSVladimir Kotal# You may not use this file except in compliance with the License. 8b2d29daeSVladimir Kotal# 9b2d29daeSVladimir Kotal# See LICENSE.txt included in this distribution for the specific 10b2d29daeSVladimir Kotal# language governing permissions and limitations under the License. 11b2d29daeSVladimir Kotal# 12b2d29daeSVladimir Kotal# When distributing Covered Code, include this CDDL HEADER in each 13b2d29daeSVladimir Kotal# file and include the License file at LICENSE.txt. 14b2d29daeSVladimir Kotal# If applicable, add the following below this CDDL HEADER, with the 15b2d29daeSVladimir Kotal# fields enclosed by brackets "[]" replaced with your own identifying 16b2d29daeSVladimir Kotal# information: Portions Copyright [yyyy] [name of copyright owner] 17b2d29daeSVladimir Kotal# 18b2d29daeSVladimir Kotal# CDDL HEADER END 19b2d29daeSVladimir Kotal 20b2d29daeSVladimir Kotal# 21b2d29daeSVladimir Kotal# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. 22b2d29daeSVladimir Kotal# 23b2d29daeSVladimir Kotal 24b2d29daeSVladimir Kotalimport os 25b2d29daeSVladimir Kotalimport logging 26b2d29daeSVladimir Kotalimport multiprocessing 27b2d29daeSVladimir Kotalimport shutil 28b2d29daeSVladimir Kotalimport subprocess 29b9361335SVladimir Kotalimport tempfile 30b2d29daeSVladimir Kotalimport threading 31b2d29daeSVladimir Kotalimport time 32d44cbea0SVladimir Kotalfrom pathlib import Path 33d44cbea0SVladimir Kotalfrom requests import get, ConnectionError 34f1835fddSVladimir Kotalfrom flask import Flask 35f1835fddSVladimir Kotalfrom flask_httpauth import HTTPTokenAuth 36f1835fddSVladimir Kotalfrom waitress import serve 37b2d29daeSVladimir Kotal 38b2d29daeSVladimir Kotalfrom opengrok_tools.utils.log import get_console_logger, \ 39b2d29daeSVladimir Kotal get_log_level, get_class_basename 40b2d29daeSVladimir Kotalfrom opengrok_tools.deploy import deploy_war 41b2d29daeSVladimir Kotalfrom opengrok_tools.utils.indexer import Indexer 42b2d29daeSVladimir Kotalfrom opengrok_tools.sync import do_sync 43b9361335SVladimir Kotalfrom opengrok_tools.config_merge import merge_config_files 44b2d29daeSVladimir Kotalfrom opengrok_tools.utils.opengrok import list_projects, \ 451822b591SVladimir Kotal add_project, delete_project, get_configuration, set_config_value 46b2d29daeSVladimir Kotalfrom opengrok_tools.utils.readconfig import read_config 47b2d29daeSVladimir Kotalfrom opengrok_tools.utils.exitvals import SUCCESS_EXITVAL 48b2d29daeSVladimir Kotal 49b2d29daeSVladimir Kotal 50b2d29daeSVladimir Kotalfs_root = os.path.abspath('.').split(os.path.sep)[0] + os.path.sep 51b2d29daeSVladimir Kotalif os.environ.get('OPENGROK_TOMCAT_ROOT'): # debug only 52b2d29daeSVladimir Kotal tomcat_root = os.environ.get('OPENGROK_TOMCAT_ROOT') 53b2d29daeSVladimir Kotalelse: 54b2d29daeSVladimir Kotal tomcat_root = os.path.join(fs_root, "usr", "local", "tomcat") 55b2d29daeSVladimir Kotal 56b2d29daeSVladimir Kotalif os.environ.get('OPENGROK_ROOT'): # debug only 57b2d29daeSVladimir Kotal OPENGROK_BASE_DIR = os.environ.get('OPENGROK_ROOT') 58b2d29daeSVladimir Kotalelse: 59b2d29daeSVladimir Kotal OPENGROK_BASE_DIR = os.path.join(fs_root, "opengrok") 60b2d29daeSVladimir Kotal 61b2d29daeSVladimir KotalOPENGROK_LIB_DIR = os.path.join(OPENGROK_BASE_DIR, "lib") 62b2d29daeSVladimir KotalOPENGROK_DATA_ROOT = os.path.join(OPENGROK_BASE_DIR, "data") 63b2d29daeSVladimir KotalOPENGROK_SRC_ROOT = os.path.join(OPENGROK_BASE_DIR, "src") 64b2d29daeSVladimir KotalBODY_INCLUDE_FILE = os.path.join(OPENGROK_DATA_ROOT, "body_include") 65b2d29daeSVladimir KotalOPENGROK_CONFIG_FILE = os.path.join(OPENGROK_BASE_DIR, "etc", 66b2d29daeSVladimir Kotal "configuration.xml") 67b2d29daeSVladimir KotalOPENGROK_WEBAPPS_DIR = os.path.join(tomcat_root, "webapps") 684810eb96SVladimir KotalOPENGROK_JAR = os.path.join(OPENGROK_LIB_DIR, 'opengrok.jar') 69b2d29daeSVladimir Kotal 70f1835fddSVladimir Kotalexpected_token = None 71f1835fddSVladimir Kotal 72f1835fddSVladimir Kotalsleep_event = threading.Event() 73f1835fddSVladimir Kotalapp = Flask(__name__) 74f1835fddSVladimir Kotalauth = HTTPTokenAuth(scheme='Bearer') 75f1835fddSVladimir Kotal 76f1835fddSVladimir Kotal 77f1835fddSVladimir Kotal@auth.verify_token 78f1835fddSVladimir Kotaldef verify_token(token): 79f1835fddSVladimir Kotal if expected_token is None: 80f1835fddSVladimir Kotal return "yes" 81f1835fddSVladimir Kotal 82f1835fddSVladimir Kotal if token is not None and token == expected_token: 83f1835fddSVladimir Kotal return "yes" 84f1835fddSVladimir Kotal 85f1835fddSVladimir Kotal 86f1835fddSVladimir Kotal@app.route('/reindex') 87f1835fddSVladimir Kotal@auth.login_required 88f1835fddSVladimir Kotaldef index(): 89f1835fddSVladimir Kotal # Signal the sync/indexer thread. 90f1835fddSVladimir Kotal sleep_event.set() 91f1835fddSVladimir Kotal sleep_event.clear() 92f1835fddSVladimir Kotal 93f1835fddSVladimir Kotal return "Reindex triggered" 94f1835fddSVladimir Kotal 95f1835fddSVladimir Kotal 96f1835fddSVladimir Kotaldef rest_function(logger, rest_port): 97f1835fddSVladimir Kotal logger.info("Starting REST app on port {}".format(rest_port)) 98f1835fddSVladimir Kotal serve(app, host="0.0.0.0", port=rest_port) 99f1835fddSVladimir Kotal 100b2d29daeSVladimir Kotal 101f7d6d77cSVladimir Kotaldef set_url_root(logger, url_root): 102b2d29daeSVladimir Kotal """ 103b2d29daeSVladimir Kotal Set URL root and URI based on input 104788f14d3SVladimir Kotal :param logger: logger instance 105b2d29daeSVladimir Kotal :param url_root: input 106b2d29daeSVladimir Kotal :return: URI and URL root 107b2d29daeSVladimir Kotal """ 108b2d29daeSVladimir Kotal if not url_root: 109b2d29daeSVladimir Kotal url_root = '/' 110b2d29daeSVladimir Kotal 111b2d29daeSVladimir Kotal if ' ' in url_root: 112b2d29daeSVladimir Kotal logger.warn('Deployment path contains spaces. Deploying to root') 113b2d29daeSVladimir Kotal url_root = '/' 114b2d29daeSVladimir Kotal 115b2d29daeSVladimir Kotal # Remove leading and trailing slashes 116b2d29daeSVladimir Kotal if url_root.startswith('/'): 117b2d29daeSVladimir Kotal url_root = url_root[1:] 118b2d29daeSVladimir Kotal if url_root.endswith('/'): 119b2d29daeSVladimir Kotal url_root = url_root[:-1] 120b2d29daeSVladimir Kotal 121b2d29daeSVladimir Kotal uri = "http://localhost:8080/" + url_root 122b2d29daeSVladimir Kotal # 123b2d29daeSVladimir Kotal # Make sure URI ends with slash. This is important for the various API 124b2d29daeSVladimir Kotal # calls, notably for those that check the HTTP error code. 125b2d29daeSVladimir Kotal # Normally accessing the URI without the terminating slash results in 126b2d29daeSVladimir Kotal # HTTP redirect (code 302) instead of success (200). 127b2d29daeSVladimir Kotal # 128b2d29daeSVladimir Kotal if not uri.endswith('/'): 129b2d29daeSVladimir Kotal uri = uri + '/' 130b2d29daeSVladimir Kotal 131b2d29daeSVladimir Kotal return uri, url_root 132b2d29daeSVladimir Kotal 133b2d29daeSVladimir Kotal 134b2d29daeSVladimir Kotaldef get_war_name(url_root): 135d44cbea0SVladimir Kotal """ 136d44cbea0SVladimir Kotal :param url_root: web app URL root 137d44cbea0SVladimir Kotal :return: filename of the WAR file 138d44cbea0SVladimir Kotal """ 139b2d29daeSVladimir Kotal if len(url_root) == 0: 140b2d29daeSVladimir Kotal return "ROOT.war" 141f7d6d77cSVladimir Kotal 142b2d29daeSVladimir Kotal return url_root + ".war" 143b2d29daeSVladimir Kotal 144b2d29daeSVladimir Kotal 145f7d6d77cSVladimir Kotaldef deploy(logger, url_root): 146b2d29daeSVladimir Kotal """ 147b2d29daeSVladimir Kotal Deploy the web application 148788f14d3SVladimir Kotal :param logger: logger instance 149b2d29daeSVladimir Kotal :param url_root: web app URL root 150b2d29daeSVladimir Kotal """ 151b2d29daeSVladimir Kotal 152b2d29daeSVladimir Kotal logger.info('Deploying web application') 153b2d29daeSVladimir Kotal webapps_dir = os.path.join(tomcat_root, 'webapps') 154b2d29daeSVladimir Kotal if not os.path.isdir(webapps_dir): 155b2d29daeSVladimir Kotal raise Exception("{} is not a directory".format(webapps_dir)) 156b2d29daeSVladimir Kotal 157b2d29daeSVladimir Kotal for item in os.listdir(webapps_dir): 158b2d29daeSVladimir Kotal subdir = os.path.join(webapps_dir, item) 159b2d29daeSVladimir Kotal if os.path.isdir(subdir): 160b2d29daeSVladimir Kotal logger.debug("Removing '{}' directory recursively".format(subdir)) 161b2d29daeSVladimir Kotal shutil.rmtree(subdir) 162b2d29daeSVladimir Kotal 163b2d29daeSVladimir Kotal deploy_war(logger, os.path.join(OPENGROK_LIB_DIR, "source.war"), 164b2d29daeSVladimir Kotal os.path.join(OPENGROK_WEBAPPS_DIR, get_war_name(url_root)), 165b2d29daeSVladimir Kotal OPENGROK_CONFIG_FILE, None) 166b2d29daeSVladimir Kotal 167b2d29daeSVladimir Kotal 168f7d6d77cSVladimir Kotaldef setup_redirect_source(logger, url_root): 169b2d29daeSVladimir Kotal """ 170b2d29daeSVladimir Kotal Set up redirect from /source 171b2d29daeSVladimir Kotal """ 172b2d29daeSVladimir Kotal logger.debug("Setting up redirect from /source to '{}'".format(url_root)) 173b2d29daeSVladimir Kotal source_dir = os.path.join(OPENGROK_WEBAPPS_DIR, "source") 174b2d29daeSVladimir Kotal if not os.path.isdir(source_dir): 175b2d29daeSVladimir Kotal os.makedirs(source_dir) 176b2d29daeSVladimir Kotal 177b2d29daeSVladimir Kotal with open(os.path.join(source_dir, "index.jsp"), "w+") as index: 178b2d29daeSVladimir Kotal index.write("<% response.sendRedirect(\"/{}\"); %>".format(url_root)) 179b2d29daeSVladimir Kotal 180b2d29daeSVladimir Kotal 181f7d6d77cSVladimir Kotaldef wait_for_tomcat(logger, uri): 182b2d29daeSVladimir Kotal """ 183b2d29daeSVladimir Kotal Active/busy waiting for Tomcat to come up. 184b2d29daeSVladimir Kotal Currently there is no upper time bound. 185b2d29daeSVladimir Kotal """ 186b2d29daeSVladimir Kotal logger.info("Waiting for Tomcat to start") 187b2d29daeSVladimir Kotal 188b2d29daeSVladimir Kotal while True: 189b2d29daeSVladimir Kotal try: 190d44cbea0SVladimir Kotal ret = get(uri) 191d44cbea0SVladimir Kotal status = ret.status_code 192b2d29daeSVladimir Kotal except ConnectionError: 193b2d29daeSVladimir Kotal status = 0 194b2d29daeSVladimir Kotal 195b2d29daeSVladimir Kotal if status != 200: 196b2d29daeSVladimir Kotal logger.debug("Got status {} for {}, sleeping for 1 second". 197b2d29daeSVladimir Kotal format(status, uri)) 198b2d29daeSVladimir Kotal time.sleep(1) 199b2d29daeSVladimir Kotal else: 200b2d29daeSVladimir Kotal break 201b2d29daeSVladimir Kotal 202b2d29daeSVladimir Kotal logger.info("Tomcat is ready") 203b2d29daeSVladimir Kotal 204b2d29daeSVladimir Kotal 205f7d6d77cSVladimir Kotaldef refresh_projects(logger, uri): 206b2d29daeSVladimir Kotal """ 207b2d29daeSVladimir Kotal Ensure each immediate source root subdirectory is a project. 208b2d29daeSVladimir Kotal """ 209b2d29daeSVladimir Kotal webapp_projects = list_projects(logger, uri) 210b2d29daeSVladimir Kotal logger.debug('Projects from the web app: {}'.format(webapp_projects)) 211b2d29daeSVladimir Kotal src_root = OPENGROK_SRC_ROOT 212b2d29daeSVladimir Kotal 213b2d29daeSVladimir Kotal # Add projects. 214b2d29daeSVladimir Kotal for item in os.listdir(src_root): 215b2d29daeSVladimir Kotal logger.debug('Got item {}'.format(item)) 216b2d29daeSVladimir Kotal if os.path.isdir(os.path.join(src_root, item)): 217b2d29daeSVladimir Kotal if item not in webapp_projects: 218b2d29daeSVladimir Kotal logger.info("Adding project {}".format(item)) 219b2d29daeSVladimir Kotal add_project(logger, item, uri) 220b2d29daeSVladimir Kotal 221b2d29daeSVladimir Kotal # Remove projects 222b2d29daeSVladimir Kotal for item in webapp_projects: 223b2d29daeSVladimir Kotal if not os.path.isdir(os.path.join(src_root, item)): 224b2d29daeSVladimir Kotal logger.info("Deleting project {}".format(item)) 225b2d29daeSVladimir Kotal delete_project(logger, item, uri) 226b2d29daeSVladimir Kotal 227b2d29daeSVladimir Kotal 228f7d6d77cSVladimir Kotaldef save_config(logger, uri, config_path): 229b2d29daeSVladimir Kotal """ 230b2d29daeSVladimir Kotal Retrieve configuration from the web app and write it to file. 231788f14d3SVladimir Kotal :param logger: logger instance 232b2d29daeSVladimir Kotal :param uri: web app URI 233b2d29daeSVladimir Kotal :param config_path: file path 234b2d29daeSVladimir Kotal """ 235b2d29daeSVladimir Kotal 236b2d29daeSVladimir Kotal logger.info('Saving configuration to {}'.format(config_path)) 237b2d29daeSVladimir Kotal config = get_configuration(logger, uri) 238b2d29daeSVladimir Kotal with open(config_path, "w+") as config_file: 239b2d29daeSVladimir Kotal config_file.write(config) 240b2d29daeSVladimir Kotal 241b2d29daeSVladimir Kotal 242b2d29daeSVladimir Kotaldef merge_commands_env(commands, env): 243b2d29daeSVladimir Kotal """ 244b2d29daeSVladimir Kotal Merge environment into command structure. If any of the commands has 245b2d29daeSVladimir Kotal an environment already set, the env is merged in. 246b2d29daeSVladimir Kotal :param commands: commands structure 247b2d29daeSVladimir Kotal :param env: environment dictionary 248b2d29daeSVladimir Kotal :return: updated commands structure 249b2d29daeSVladimir Kotal """ 250b2d29daeSVladimir Kotal for cmd in commands: 251b2d29daeSVladimir Kotal cmd_env = cmd.get('env') 252b2d29daeSVladimir Kotal if cmd_env: 253b2d29daeSVladimir Kotal cmd.env.update(env) 254b2d29daeSVladimir Kotal else: 255b2d29daeSVladimir Kotal cmd['env'] = env 256b2d29daeSVladimir Kotal 257b2d29daeSVladimir Kotal return commands 258b2d29daeSVladimir Kotal 259b2d29daeSVladimir Kotal 2604810eb96SVladimir Kotaldef indexer_no_projects(logger, uri, config_path, sync_period, 2614810eb96SVladimir Kotal extra_indexer_options): 2624810eb96SVladimir Kotal """ 2634810eb96SVladimir Kotal Project less indexer 2644810eb96SVladimir Kotal """ 2654810eb96SVladimir Kotal 2664810eb96SVladimir Kotal wait_for_tomcat(logger, uri) 2674810eb96SVladimir Kotal 268f1835fddSVladimir Kotal periodic_sync = True 269f1835fddSVladimir Kotal if sync_period is None or sync_period == 0: 270f1835fddSVladimir Kotal periodic_sync = False 271f1835fddSVladimir Kotal 2724810eb96SVladimir Kotal while True: 273f1835fddSVladimir Kotal if not periodic_sync: 274f1835fddSVladimir Kotal sleep_event.wait() 275f1835fddSVladimir Kotal 2764810eb96SVladimir Kotal indexer_options = ['-s', OPENGROK_SRC_ROOT, 2774810eb96SVladimir Kotal '-d', OPENGROK_DATA_ROOT, 2784810eb96SVladimir Kotal '-c', '/usr/local/bin/ctags', 2794810eb96SVladimir Kotal '--remote', 'on', 2804810eb96SVladimir Kotal '-H', 2814810eb96SVladimir Kotal '-W', config_path, 2824810eb96SVladimir Kotal '-U', uri] 2834810eb96SVladimir Kotal if extra_indexer_options: 2844810eb96SVladimir Kotal logger.debug("Adding extra indexer options: {}". 2854810eb96SVladimir Kotal format(extra_indexer_options)) 2864810eb96SVladimir Kotal indexer_options.extend(extra_indexer_options.split()) 2874810eb96SVladimir Kotal indexer = Indexer(indexer_options, logger=logger, 288d2f093daSVladimir Kotal jar=OPENGROK_JAR, doprint=True) 2894810eb96SVladimir Kotal indexer.execute() 2904810eb96SVladimir Kotal 291f1835fddSVladimir Kotal if periodic_sync: 2924810eb96SVladimir Kotal sleep_seconds = sync_period * 60 2934810eb96SVladimir Kotal logger.info("Sleeping for {} seconds".format(sleep_seconds)) 2944810eb96SVladimir Kotal time.sleep(sleep_seconds) 2954810eb96SVladimir Kotal 2964810eb96SVladimir Kotal 2974810eb96SVladimir Kotaldef project_syncer(logger, loglevel, uri, config_path, sync_period, 2984810eb96SVladimir Kotal numworkers, env): 299b2d29daeSVladimir Kotal """ 300b2d29daeSVladimir Kotal Wrapper for running opengrok-sync. 301b2d29daeSVladimir Kotal To be run in a thread/process in the background. 302b2d29daeSVladimir Kotal """ 303b2d29daeSVladimir Kotal 304f7d6d77cSVladimir Kotal wait_for_tomcat(logger, uri) 305b2d29daeSVladimir Kotal 3061822b591SVladimir Kotal set_config_value(logger, 'projectsEnabled', 'true', uri) 3071822b591SVladimir Kotal 308f1835fddSVladimir Kotal periodic_sync = True 309f1835fddSVladimir Kotal if sync_period is None or sync_period == 0: 310f1835fddSVladimir Kotal periodic_sync = False 311f1835fddSVladimir Kotal 312b2d29daeSVladimir Kotal while True: 313f1835fddSVladimir Kotal if not periodic_sync: 314f1835fddSVladimir Kotal sleep_event.wait() 315f1835fddSVladimir Kotal 316f7d6d77cSVladimir Kotal refresh_projects(logger, uri) 317b2d29daeSVladimir Kotal 318b2d29daeSVladimir Kotal if os.environ.get('OPENGROK_SYNC_YML'): # debug only 319b2d29daeSVladimir Kotal config_file = os.environ.get('OPENGROK_SYNC_YML') 320b2d29daeSVladimir Kotal else: 321b2d29daeSVladimir Kotal config_file = os.path.join(fs_root, 'scripts', 'sync.yml') 322b2d29daeSVladimir Kotal config = read_config(logger, config_file) 323b2d29daeSVladimir Kotal if config is None: 324b2d29daeSVladimir Kotal logger.error("Cannot read config file from {}".format(config_file)) 325b2d29daeSVladimir Kotal raise Exception("no sync config") 326b2d29daeSVladimir Kotal 327b2d29daeSVladimir Kotal projects = list_projects(logger, uri) 328b2d29daeSVladimir Kotal # 329b2d29daeSVladimir Kotal # The driveon=True is needed for the initial indexing of newly 330b2d29daeSVladimir Kotal # added project, otherwise the incoming check in the opengrok-mirror 3313d5852a4SVladimir Kotal # program would short circuit it. 332b2d29daeSVladimir Kotal # 333b2d29daeSVladimir Kotal if env: 334b2d29daeSVladimir Kotal logger.info('Merging commands with environment') 335b2d29daeSVladimir Kotal commands = merge_commands_env(config["commands"], env) 336b2d29daeSVladimir Kotal logger.debug(config['commands']) 337b2d29daeSVladimir Kotal else: 338b2d29daeSVladimir Kotal commands = config["commands"] 339b2d29daeSVladimir Kotal 340b2d29daeSVladimir Kotal logger.info("Sync starting") 341b2d29daeSVladimir Kotal do_sync(loglevel, commands, config.get('cleanup'), 342b2d29daeSVladimir Kotal projects, config.get("ignore_errors"), uri, 343b2d29daeSVladimir Kotal numworkers, driveon=True, logger=logger, print_output=True) 344b2d29daeSVladimir Kotal logger.info("Sync done") 345b2d29daeSVladimir Kotal 346b2d29daeSVladimir Kotal # Workaround for https://github.com/oracle/opengrok/issues/1670 347b2d29daeSVladimir Kotal Path(os.path.join(OPENGROK_DATA_ROOT, 'timestamp')).touch() 348b2d29daeSVladimir Kotal 349f7d6d77cSVladimir Kotal save_config(logger, uri, config_path) 350b2d29daeSVladimir Kotal 351f1835fddSVladimir Kotal if periodic_sync: 3523d5852a4SVladimir Kotal sleep_seconds = sync_period * 60 353b2d29daeSVladimir Kotal logger.info("Sleeping for {} seconds".format(sleep_seconds)) 354b2d29daeSVladimir Kotal time.sleep(sleep_seconds) 355b2d29daeSVladimir Kotal 356b2d29daeSVladimir Kotal 3579d6d7c28SVladimir Kotaldef create_bare_config(logger, extra_indexer_options=None): 358b2d29daeSVladimir Kotal """ 359b2d29daeSVladimir Kotal Create bare configuration file with a few basic settings. 360b2d29daeSVladimir Kotal """ 361b2d29daeSVladimir Kotal 362b2d29daeSVladimir Kotal logger.info('Creating bare configuration in {}'. 363b2d29daeSVladimir Kotal format(OPENGROK_CONFIG_FILE)) 3644810eb96SVladimir Kotal indexer_options = ['-s', OPENGROK_SRC_ROOT, 365b2d29daeSVladimir Kotal '-d', OPENGROK_DATA_ROOT, 366b2d29daeSVladimir Kotal '-c', '/usr/local/bin/ctags', 367b2d29daeSVladimir Kotal '--remote', 'on', 3684810eb96SVladimir Kotal '-H', 369b2d29daeSVladimir Kotal '-W', OPENGROK_CONFIG_FILE, 3704810eb96SVladimir Kotal '--noIndex'] 3714810eb96SVladimir Kotal 3729d6d7c28SVladimir Kotal if extra_indexer_options: 3735b279385SVladimir Kotal if type(extra_indexer_options) is not list: 3745b279385SVladimir Kotal raise Exception("extra_indexer_options has to be a list") 3759d6d7c28SVladimir Kotal indexer_options.extend(extra_indexer_options) 3764810eb96SVladimir Kotal indexer = Indexer(indexer_options, 3774810eb96SVladimir Kotal jar=OPENGROK_JAR, 378b2d29daeSVladimir Kotal logger=logger, doprint=True) 379b2d29daeSVladimir Kotal indexer.execute() 380b2d29daeSVladimir Kotal ret = indexer.getretcode() 381b2d29daeSVladimir Kotal if ret != SUCCESS_EXITVAL: 382b2d29daeSVladimir Kotal logger.error('Command returned {}'.format(ret)) 383b2d29daeSVladimir Kotal logger.error(indexer.geterroutput()) 384b2d29daeSVladimir Kotal raise Exception("Failed to create bare configuration") 385b2d29daeSVladimir Kotal 386b2d29daeSVladimir Kotal 387f1835fddSVladimir Kotaldef get_num_from_env(logger, env_name, default_value): 388f1835fddSVladimir Kotal value = default_value 389f1835fddSVladimir Kotal env_str = os.environ.get(env_name) 390f1835fddSVladimir Kotal if env_str: 391f1835fddSVladimir Kotal try: 392f1835fddSVladimir Kotal n = int(env_str) 393f1835fddSVladimir Kotal if n >= 0: 394f1835fddSVladimir Kotal value = n 395f1835fddSVladimir Kotal except ValueError: 396f1835fddSVladimir Kotal logger.error("{} is not a number: {}". 397f1835fddSVladimir Kotal format(env_name, env_str)) 398f1835fddSVladimir Kotal 399f1835fddSVladimir Kotal return value 400f1835fddSVladimir Kotal 401f1835fddSVladimir Kotal 402f7d6d77cSVladimir Kotaldef main(): 403b2d29daeSVladimir Kotal log_level = os.environ.get('OPENGROK_LOG_LEVEL') 404b2d29daeSVladimir Kotal if log_level: 405b2d29daeSVladimir Kotal log_level = get_log_level(log_level) 406b2d29daeSVladimir Kotal else: 407b2d29daeSVladimir Kotal log_level = logging.INFO 408b2d29daeSVladimir Kotal 409b2d29daeSVladimir Kotal logger = get_console_logger(get_class_basename(), log_level) 410b2d29daeSVladimir Kotal 411f7d6d77cSVladimir Kotal uri, url_root = set_url_root(logger, os.environ.get('URL_ROOT')) 412f7d6d77cSVladimir Kotal logger.debug("URL_ROOT = {}".format(url_root)) 413f7d6d77cSVladimir Kotal logger.debug("URI = {}".format(uri)) 414b2d29daeSVladimir Kotal 4153d5852a4SVladimir Kotal # default period for syncing (in minutes) 416f1835fddSVladimir Kotal sync_period = get_num_from_env(logger, 'SYNC_TIME_MINUTES', 10) 4173d5852a4SVladimir Kotal if sync_period == 0: 4183d5852a4SVladimir Kotal logger.info("synchronization disabled") 419b2d29daeSVladimir Kotal else: 4203d5852a4SVladimir Kotal logger.info("synchronization period = {} minutes".format(sync_period)) 421b2d29daeSVladimir Kotal 422b2d29daeSVladimir Kotal # Note that deploy is done before Tomcat is started. 423f7d6d77cSVladimir Kotal deploy(logger, url_root) 424b2d29daeSVladimir Kotal 425f7d6d77cSVladimir Kotal if url_root != '/source': 426f7d6d77cSVladimir Kotal setup_redirect_source(logger, url_root) 427b2d29daeSVladimir Kotal 428b2d29daeSVladimir Kotal env = {} 429dadacd3dSAdam Hornacek extra_indexer_options = os.environ.get('INDEXER_OPT', '') 4304810eb96SVladimir Kotal if extra_indexer_options: 4314810eb96SVladimir Kotal logger.info("extra indexer options: {}".format(extra_indexer_options)) 4324810eb96SVladimir Kotal env['OPENGROK_INDEXER_OPTIONAL_ARGS'] = extra_indexer_options 433b2d29daeSVladimir Kotal if os.environ.get('NOMIRROR'): 434b2d29daeSVladimir Kotal env['OPENGROK_NO_MIRROR'] = os.environ.get('NOMIRROR') 435b2d29daeSVladimir Kotal logger.debug('Extra environment: {}'.format(env)) 436b2d29daeSVladimir Kotal 4374810eb96SVladimir Kotal use_projects = True 4384810eb96SVladimir Kotal if os.environ.get('AVOID_PROJECTS'): 4394810eb96SVladimir Kotal use_projects = False 4404810eb96SVladimir Kotal 441b2d29daeSVladimir Kotal # 442b2d29daeSVladimir Kotal # Create empty configuration to avoid the non existent file exception 443b2d29daeSVladimir Kotal # in the web app during the first web app startup. 444b2d29daeSVladimir Kotal # 445b2d29daeSVladimir Kotal if not os.path.exists(OPENGROK_CONFIG_FILE) or \ 446b2d29daeSVladimir Kotal os.path.getsize(OPENGROK_CONFIG_FILE) == 0: 447c6f6c3d1SVladimir Kotal create_bare_config(logger, extra_indexer_options.split()) 448b2d29daeSVladimir Kotal 449b9361335SVladimir Kotal # 450b9361335SVladimir Kotal # If there is read-only configuration file, merge it with current 451b9361335SVladimir Kotal # configuration. 452b9361335SVladimir Kotal # 453b9361335SVladimir Kotal read_only_config_file = os.environ.get('READONLY_CONFIG_FILE') 454b9361335SVladimir Kotal if read_only_config_file and os.path.exists(read_only_config_file): 455b9361335SVladimir Kotal logger.info('Merging read-only configuration from \'{}\' with current ' 456b9361335SVladimir Kotal 'configuration in \'{}\''.format(read_only_config_file, 457b9361335SVladimir Kotal OPENGROK_CONFIG_FILE)) 458b9361335SVladimir Kotal out_file = None 459b9361335SVladimir Kotal with tempfile.NamedTemporaryFile(mode='w+', delete=False, 460b9361335SVladimir Kotal prefix='merged_config') as tmp_out: 461*70e4aafbSVladimir Kotal out_file = tmp_out.name 462b9361335SVladimir Kotal merge_config_files(read_only_config_file, OPENGROK_CONFIG_FILE, 463b9361335SVladimir Kotal tmp_out, jar=OPENGROK_JAR, loglevel=log_level) 464b9361335SVladimir Kotal 465b9361335SVladimir Kotal if out_file and os.path.getsize(out_file) > 0: 466b9361335SVladimir Kotal shutil.move(tmp_out.name, OPENGROK_CONFIG_FILE) 467b9361335SVladimir Kotal else: 468b9361335SVladimir Kotal logger.warning('Failed to merge read-only configuration, ' 469b9361335SVladimir Kotal 'leaving the original in place') 470*70e4aafbSVladimir Kotal if out_file: 471*70e4aafbSVladimir Kotal os.remove(out_file) 472b9361335SVladimir Kotal 4734810eb96SVladimir Kotal if use_projects: 474f1835fddSVladimir Kotal num_workers = get_num_from_env(logger, 'WORKERS', 475f1835fddSVladimir Kotal multiprocessing.cpu_count()) 476b2d29daeSVladimir Kotal logger.info('Number of sync workers: {}'.format(num_workers)) 477b2d29daeSVladimir Kotal 4784810eb96SVladimir Kotal worker_function = project_syncer 4794810eb96SVladimir Kotal syncer_args = (logger, log_level, uri, 480f7d6d77cSVladimir Kotal OPENGROK_CONFIG_FILE, 4814810eb96SVladimir Kotal sync_period, num_workers, env) 4824810eb96SVladimir Kotal else: 4834810eb96SVladimir Kotal worker_function = indexer_no_projects 4844810eb96SVladimir Kotal syncer_args = (logger, uri, OPENGROK_CONFIG_FILE, sync_period, 4854810eb96SVladimir Kotal extra_indexer_options) 4864810eb96SVladimir Kotal 4874810eb96SVladimir Kotal logger.debug("Starting sync thread") 4884810eb96SVladimir Kotal thread = threading.Thread(target=worker_function, name="Sync thread", 4894810eb96SVladimir Kotal args=syncer_args) 490f7d6d77cSVladimir Kotal thread.start() 491b2d29daeSVladimir Kotal 492f1835fddSVladimir Kotal rest_port = get_num_from_env(logger, 'REST_PORT', 5000) 493f1835fddSVladimir Kotal token = os.environ.get('REST_TOKEN') 494f1835fddSVladimir Kotal global expected_token 495f1835fddSVladimir Kotal if token: 496f1835fddSVladimir Kotal logger.debug("Setting expected token for REST endpoint" 497f1835fddSVladimir Kotal "on port {} to '{}'".format(rest_port, token)) 498f1835fddSVladimir Kotal expected_token = token 499f1835fddSVladimir Kotal logger.debug("Starting REST thread") 500f1835fddSVladimir Kotal thread = threading.Thread(target=rest_function, name="REST thread", 501f1835fddSVladimir Kotal args=(logger, rest_port)) 502f1835fddSVladimir Kotal thread.start() 503f1835fddSVladimir Kotal 504b2d29daeSVladimir Kotal # Start Tomcat last. It will be the foreground process. 505b2d29daeSVladimir Kotal logger.info("Starting Tomcat") 506b2d29daeSVladimir Kotal subprocess.run([os.path.join(tomcat_root, 'bin', 'catalina.sh'), 'run']) 507f7d6d77cSVladimir Kotal 508f7d6d77cSVladimir Kotal 509f7d6d77cSVladimir Kotalif __name__ == "__main__": 510f7d6d77cSVladimir Kotal main() 511