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 296100daaaSVladimir Kotalimport sys 30b9361335SVladimir Kotalimport tempfile 31b2d29daeSVladimir Kotalimport threading 32b2d29daeSVladimir Kotalimport time 33d44cbea0SVladimir Kotalfrom pathlib import Path 34d44cbea0SVladimir Kotalfrom requests import get, ConnectionError 35f1835fddSVladimir Kotalfrom flask import Flask 36f1835fddSVladimir Kotalfrom flask_httpauth import HTTPTokenAuth 37f1835fddSVladimir Kotalfrom waitress import serve 38b2d29daeSVladimir Kotal 39b2d29daeSVladimir Kotalfrom opengrok_tools.utils.log import get_console_logger, \ 40b2d29daeSVladimir Kotal get_log_level, get_class_basename 41b2d29daeSVladimir Kotalfrom opengrok_tools.deploy import deploy_war 42b2d29daeSVladimir Kotalfrom opengrok_tools.utils.indexer import Indexer 43b2d29daeSVladimir Kotalfrom opengrok_tools.sync import do_sync 44b9361335SVladimir Kotalfrom opengrok_tools.config_merge import merge_config_files 45b2d29daeSVladimir Kotalfrom opengrok_tools.utils.opengrok import list_projects, \ 46*19f6e235SVladimir Kotal add_project, delete_project, get_configuration 47b2d29daeSVladimir Kotalfrom opengrok_tools.utils.readconfig import read_config 48b2d29daeSVladimir Kotalfrom opengrok_tools.utils.exitvals import SUCCESS_EXITVAL 49b2d29daeSVladimir Kotal 50b2d29daeSVladimir Kotal 51b2d29daeSVladimir Kotalfs_root = os.path.abspath('.').split(os.path.sep)[0] + os.path.sep 52b2d29daeSVladimir Kotalif os.environ.get('OPENGROK_TOMCAT_ROOT'): # debug only 53b2d29daeSVladimir Kotal tomcat_root = os.environ.get('OPENGROK_TOMCAT_ROOT') 54b2d29daeSVladimir Kotalelse: 55b2d29daeSVladimir Kotal tomcat_root = os.path.join(fs_root, "usr", "local", "tomcat") 56b2d29daeSVladimir Kotal 57b2d29daeSVladimir Kotalif os.environ.get('OPENGROK_ROOT'): # debug only 58b2d29daeSVladimir Kotal OPENGROK_BASE_DIR = os.environ.get('OPENGROK_ROOT') 59b2d29daeSVladimir Kotalelse: 60b2d29daeSVladimir Kotal OPENGROK_BASE_DIR = os.path.join(fs_root, "opengrok") 61b2d29daeSVladimir Kotal 62b2d29daeSVladimir KotalOPENGROK_LIB_DIR = os.path.join(OPENGROK_BASE_DIR, "lib") 63b2d29daeSVladimir KotalOPENGROK_DATA_ROOT = os.path.join(OPENGROK_BASE_DIR, "data") 64b2d29daeSVladimir KotalOPENGROK_SRC_ROOT = os.path.join(OPENGROK_BASE_DIR, "src") 65b2d29daeSVladimir KotalBODY_INCLUDE_FILE = os.path.join(OPENGROK_DATA_ROOT, "body_include") 66b2d29daeSVladimir KotalOPENGROK_CONFIG_FILE = os.path.join(OPENGROK_BASE_DIR, "etc", 67b2d29daeSVladimir Kotal "configuration.xml") 68b2d29daeSVladimir KotalOPENGROK_WEBAPPS_DIR = os.path.join(tomcat_root, "webapps") 694810eb96SVladimir KotalOPENGROK_JAR = os.path.join(OPENGROK_LIB_DIR, 'opengrok.jar') 70b2d29daeSVladimir Kotal 71f1835fddSVladimir Kotalexpected_token = None 72f1835fddSVladimir Kotal 73f1835fddSVladimir Kotalsleep_event = threading.Event() 74f1835fddSVladimir Kotalapp = Flask(__name__) 75f1835fddSVladimir Kotalauth = HTTPTokenAuth(scheme='Bearer') 76f1835fddSVladimir Kotal 77f1835fddSVladimir Kotal 78f1835fddSVladimir Kotal@auth.verify_token 79f1835fddSVladimir Kotaldef verify_token(token): 80f1835fddSVladimir Kotal if expected_token is None: 81f1835fddSVladimir Kotal return "yes" 82f1835fddSVladimir Kotal 83f1835fddSVladimir Kotal if token is not None and token == expected_token: 84f1835fddSVladimir Kotal return "yes" 85f1835fddSVladimir Kotal 86f1835fddSVladimir Kotal 87f1835fddSVladimir Kotal@app.route('/reindex') 88f1835fddSVladimir Kotal@auth.login_required 89f1835fddSVladimir Kotaldef index(): 90f1835fddSVladimir Kotal # Signal the sync/indexer thread. 91f1835fddSVladimir Kotal sleep_event.set() 92f1835fddSVladimir Kotal sleep_event.clear() 93f1835fddSVladimir Kotal 94f1835fddSVladimir Kotal return "Reindex triggered" 95f1835fddSVladimir Kotal 96f1835fddSVladimir Kotal 97f1835fddSVladimir Kotaldef rest_function(logger, rest_port): 98f1835fddSVladimir Kotal logger.info("Starting REST app on port {}".format(rest_port)) 99f1835fddSVladimir Kotal serve(app, host="0.0.0.0", port=rest_port) 100f1835fddSVladimir Kotal 101b2d29daeSVladimir Kotal 102f7d6d77cSVladimir Kotaldef set_url_root(logger, url_root): 103b2d29daeSVladimir Kotal """ 104b2d29daeSVladimir Kotal Set URL root and URI based on input 105788f14d3SVladimir Kotal :param logger: logger instance 106b2d29daeSVladimir Kotal :param url_root: input 107b2d29daeSVladimir Kotal :return: URI and URL root 108b2d29daeSVladimir Kotal """ 109b2d29daeSVladimir Kotal if not url_root: 110b2d29daeSVladimir Kotal url_root = '/' 111b2d29daeSVladimir Kotal 112b2d29daeSVladimir Kotal if ' ' in url_root: 113b2d29daeSVladimir Kotal logger.warn('Deployment path contains spaces. Deploying to root') 114b2d29daeSVladimir Kotal url_root = '/' 115b2d29daeSVladimir Kotal 116b2d29daeSVladimir Kotal # Remove leading and trailing slashes 117b2d29daeSVladimir Kotal if url_root.startswith('/'): 118b2d29daeSVladimir Kotal url_root = url_root[1:] 119b2d29daeSVladimir Kotal if url_root.endswith('/'): 120b2d29daeSVladimir Kotal url_root = url_root[:-1] 121b2d29daeSVladimir Kotal 122b2d29daeSVladimir Kotal uri = "http://localhost:8080/" + url_root 123b2d29daeSVladimir Kotal # 124b2d29daeSVladimir Kotal # Make sure URI ends with slash. This is important for the various API 125b2d29daeSVladimir Kotal # calls, notably for those that check the HTTP error code. 126b2d29daeSVladimir Kotal # Normally accessing the URI without the terminating slash results in 127b2d29daeSVladimir Kotal # HTTP redirect (code 302) instead of success (200). 128b2d29daeSVladimir Kotal # 129b2d29daeSVladimir Kotal if not uri.endswith('/'): 130b2d29daeSVladimir Kotal uri = uri + '/' 131b2d29daeSVladimir Kotal 132b2d29daeSVladimir Kotal return uri, url_root 133b2d29daeSVladimir Kotal 134b2d29daeSVladimir Kotal 135b2d29daeSVladimir Kotaldef get_war_name(url_root): 136d44cbea0SVladimir Kotal """ 137d44cbea0SVladimir Kotal :param url_root: web app URL root 138d44cbea0SVladimir Kotal :return: filename of the WAR file 139d44cbea0SVladimir Kotal """ 140b2d29daeSVladimir Kotal if len(url_root) == 0: 141b2d29daeSVladimir Kotal return "ROOT.war" 142f7d6d77cSVladimir Kotal 143b2d29daeSVladimir Kotal return url_root + ".war" 144b2d29daeSVladimir Kotal 145b2d29daeSVladimir Kotal 146f7d6d77cSVladimir Kotaldef deploy(logger, url_root): 147b2d29daeSVladimir Kotal """ 148b2d29daeSVladimir Kotal Deploy the web application 149788f14d3SVladimir Kotal :param logger: logger instance 150b2d29daeSVladimir Kotal :param url_root: web app URL root 151b2d29daeSVladimir Kotal """ 152b2d29daeSVladimir Kotal 153b2d29daeSVladimir Kotal logger.info('Deploying web application') 154b2d29daeSVladimir Kotal webapps_dir = os.path.join(tomcat_root, 'webapps') 155b2d29daeSVladimir Kotal if not os.path.isdir(webapps_dir): 156b2d29daeSVladimir Kotal raise Exception("{} is not a directory".format(webapps_dir)) 157b2d29daeSVladimir Kotal 158b2d29daeSVladimir Kotal for item in os.listdir(webapps_dir): 159b2d29daeSVladimir Kotal subdir = os.path.join(webapps_dir, item) 160b2d29daeSVladimir Kotal if os.path.isdir(subdir): 161b2d29daeSVladimir Kotal logger.debug("Removing '{}' directory recursively".format(subdir)) 162b2d29daeSVladimir Kotal shutil.rmtree(subdir) 163b2d29daeSVladimir Kotal 164b2d29daeSVladimir Kotal deploy_war(logger, os.path.join(OPENGROK_LIB_DIR, "source.war"), 165b2d29daeSVladimir Kotal os.path.join(OPENGROK_WEBAPPS_DIR, get_war_name(url_root)), 166b2d29daeSVladimir Kotal OPENGROK_CONFIG_FILE, None) 167b2d29daeSVladimir Kotal 168b2d29daeSVladimir Kotal 169f7d6d77cSVladimir Kotaldef setup_redirect_source(logger, url_root): 170b2d29daeSVladimir Kotal """ 171b2d29daeSVladimir Kotal Set up redirect from /source 172b2d29daeSVladimir Kotal """ 173b2d29daeSVladimir Kotal logger.debug("Setting up redirect from /source to '{}'".format(url_root)) 174b2d29daeSVladimir Kotal source_dir = os.path.join(OPENGROK_WEBAPPS_DIR, "source") 175b2d29daeSVladimir Kotal if not os.path.isdir(source_dir): 176b2d29daeSVladimir Kotal os.makedirs(source_dir) 177b2d29daeSVladimir Kotal 178b2d29daeSVladimir Kotal with open(os.path.join(source_dir, "index.jsp"), "w+") as index: 179b2d29daeSVladimir Kotal index.write("<% response.sendRedirect(\"/{}\"); %>".format(url_root)) 180b2d29daeSVladimir Kotal 181b2d29daeSVladimir Kotal 182f7d6d77cSVladimir Kotaldef wait_for_tomcat(logger, uri): 183b2d29daeSVladimir Kotal """ 184b2d29daeSVladimir Kotal Active/busy waiting for Tomcat to come up. 185b2d29daeSVladimir Kotal Currently there is no upper time bound. 186b2d29daeSVladimir Kotal """ 187b2d29daeSVladimir Kotal logger.info("Waiting for Tomcat to start") 188b2d29daeSVladimir Kotal 189b2d29daeSVladimir Kotal while True: 190b2d29daeSVladimir Kotal try: 191d44cbea0SVladimir Kotal ret = get(uri) 192d44cbea0SVladimir Kotal status = ret.status_code 193b2d29daeSVladimir Kotal except ConnectionError: 194b2d29daeSVladimir Kotal status = 0 195b2d29daeSVladimir Kotal 196b2d29daeSVladimir Kotal if status != 200: 197b2d29daeSVladimir Kotal logger.debug("Got status {} for {}, sleeping for 1 second". 198b2d29daeSVladimir Kotal format(status, uri)) 199b2d29daeSVladimir Kotal time.sleep(1) 200b2d29daeSVladimir Kotal else: 201b2d29daeSVladimir Kotal break 202b2d29daeSVladimir Kotal 203b2d29daeSVladimir Kotal logger.info("Tomcat is ready") 204b2d29daeSVladimir Kotal 205b2d29daeSVladimir Kotal 206f7d6d77cSVladimir Kotaldef refresh_projects(logger, uri): 207b2d29daeSVladimir Kotal """ 208b2d29daeSVladimir Kotal Ensure each immediate source root subdirectory is a project. 209b2d29daeSVladimir Kotal """ 210b2d29daeSVladimir Kotal webapp_projects = list_projects(logger, uri) 2116100daaaSVladimir Kotal if not webapp_projects: 2126100daaaSVladimir Kotal return 2136100daaaSVladimir Kotal 214b2d29daeSVladimir Kotal logger.debug('Projects from the web app: {}'.format(webapp_projects)) 215b2d29daeSVladimir Kotal src_root = OPENGROK_SRC_ROOT 216b2d29daeSVladimir Kotal 217b2d29daeSVladimir Kotal # Add projects. 218b2d29daeSVladimir Kotal for item in os.listdir(src_root): 219b2d29daeSVladimir Kotal logger.debug('Got item {}'.format(item)) 220b2d29daeSVladimir Kotal if os.path.isdir(os.path.join(src_root, item)): 221b2d29daeSVladimir Kotal if item not in webapp_projects: 222b2d29daeSVladimir Kotal logger.info("Adding project {}".format(item)) 223b2d29daeSVladimir Kotal add_project(logger, item, uri) 224b2d29daeSVladimir Kotal 225b2d29daeSVladimir Kotal # Remove projects 226b2d29daeSVladimir Kotal for item in webapp_projects: 227b2d29daeSVladimir Kotal if not os.path.isdir(os.path.join(src_root, item)): 228b2d29daeSVladimir Kotal logger.info("Deleting project {}".format(item)) 229b2d29daeSVladimir Kotal delete_project(logger, item, uri) 230b2d29daeSVladimir Kotal 231b2d29daeSVladimir Kotal 232f7d6d77cSVladimir Kotaldef save_config(logger, uri, config_path): 233b2d29daeSVladimir Kotal """ 234b2d29daeSVladimir Kotal Retrieve configuration from the web app and write it to file. 235788f14d3SVladimir Kotal :param logger: logger instance 236b2d29daeSVladimir Kotal :param uri: web app URI 237b2d29daeSVladimir Kotal :param config_path: file path 238b2d29daeSVladimir Kotal """ 239b2d29daeSVladimir Kotal 240b2d29daeSVladimir Kotal logger.info('Saving configuration to {}'.format(config_path)) 241b2d29daeSVladimir Kotal config = get_configuration(logger, uri) 242b2d29daeSVladimir Kotal with open(config_path, "w+") as config_file: 243b2d29daeSVladimir Kotal config_file.write(config) 244b2d29daeSVladimir Kotal 245b2d29daeSVladimir Kotal 246b2d29daeSVladimir Kotaldef merge_commands_env(commands, env): 247b2d29daeSVladimir Kotal """ 248b2d29daeSVladimir Kotal Merge environment into command structure. If any of the commands has 249b2d29daeSVladimir Kotal an environment already set, the env is merged in. 250b2d29daeSVladimir Kotal :param commands: commands structure 251b2d29daeSVladimir Kotal :param env: environment dictionary 252b2d29daeSVladimir Kotal :return: updated commands structure 253b2d29daeSVladimir Kotal """ 254b2d29daeSVladimir Kotal for cmd in commands: 255b2d29daeSVladimir Kotal cmd_env = cmd.get('env') 256b2d29daeSVladimir Kotal if cmd_env: 257b2d29daeSVladimir Kotal cmd.env.update(env) 258b2d29daeSVladimir Kotal else: 259b2d29daeSVladimir Kotal cmd['env'] = env 260b2d29daeSVladimir Kotal 261b2d29daeSVladimir Kotal return commands 262b2d29daeSVladimir Kotal 263b2d29daeSVladimir Kotal 2644810eb96SVladimir Kotaldef indexer_no_projects(logger, uri, config_path, sync_period, 2654810eb96SVladimir Kotal extra_indexer_options): 2664810eb96SVladimir Kotal """ 2674810eb96SVladimir Kotal Project less indexer 2684810eb96SVladimir Kotal """ 2694810eb96SVladimir Kotal 2704810eb96SVladimir Kotal wait_for_tomcat(logger, uri) 2714810eb96SVladimir Kotal 272f1835fddSVladimir Kotal periodic_sync = True 273f1835fddSVladimir Kotal if sync_period is None or sync_period == 0: 274f1835fddSVladimir Kotal periodic_sync = False 275f1835fddSVladimir Kotal 2764810eb96SVladimir Kotal while True: 2774810eb96SVladimir Kotal indexer_options = ['-s', OPENGROK_SRC_ROOT, 2784810eb96SVladimir Kotal '-d', OPENGROK_DATA_ROOT, 2794810eb96SVladimir Kotal '-c', '/usr/local/bin/ctags', 2804810eb96SVladimir Kotal '--remote', 'on', 2814810eb96SVladimir Kotal '-H', 2824810eb96SVladimir Kotal '-W', config_path, 2834810eb96SVladimir Kotal '-U', uri] 2844810eb96SVladimir Kotal if extra_indexer_options: 2854810eb96SVladimir Kotal logger.debug("Adding extra indexer options: {}". 2864810eb96SVladimir Kotal format(extra_indexer_options)) 2874810eb96SVladimir Kotal indexer_options.extend(extra_indexer_options.split()) 2884810eb96SVladimir Kotal indexer = Indexer(indexer_options, logger=logger, 289d2f093daSVladimir Kotal jar=OPENGROK_JAR, doprint=True) 2904810eb96SVladimir Kotal indexer.execute() 2914810eb96SVladimir Kotal 292f1835fddSVladimir Kotal if periodic_sync: 2934810eb96SVladimir Kotal sleep_seconds = sync_period * 60 2944810eb96SVladimir Kotal logger.info("Sleeping for {} seconds".format(sleep_seconds)) 2954810eb96SVladimir Kotal time.sleep(sleep_seconds) 296b531e965SVladimir Kotal elif not periodic_sync: 297b531e965SVladimir Kotal sleep_event.wait() 2984810eb96SVladimir Kotal 2994810eb96SVladimir Kotal 3004810eb96SVladimir Kotaldef project_syncer(logger, loglevel, uri, config_path, sync_period, 3014810eb96SVladimir Kotal numworkers, env): 302b2d29daeSVladimir Kotal """ 303b2d29daeSVladimir Kotal Wrapper for running opengrok-sync. 304b2d29daeSVladimir Kotal To be run in a thread/process in the background. 305b2d29daeSVladimir Kotal """ 306b2d29daeSVladimir Kotal 307f7d6d77cSVladimir Kotal wait_for_tomcat(logger, uri) 308b2d29daeSVladimir Kotal 309f1835fddSVladimir Kotal periodic_sync = True 310f1835fddSVladimir Kotal if sync_period is None or sync_period == 0: 311f1835fddSVladimir Kotal periodic_sync = False 312f1835fddSVladimir Kotal 313b2d29daeSVladimir Kotal while True: 314f7d6d77cSVladimir Kotal refresh_projects(logger, uri) 315b2d29daeSVladimir Kotal 316b2d29daeSVladimir Kotal if os.environ.get('OPENGROK_SYNC_YML'): # debug only 317b2d29daeSVladimir Kotal config_file = os.environ.get('OPENGROK_SYNC_YML') 318b2d29daeSVladimir Kotal else: 319b2d29daeSVladimir Kotal config_file = os.path.join(fs_root, 'scripts', 'sync.yml') 320b2d29daeSVladimir Kotal config = read_config(logger, config_file) 321b2d29daeSVladimir Kotal if config is None: 322b2d29daeSVladimir Kotal logger.error("Cannot read config file from {}".format(config_file)) 323b2d29daeSVladimir Kotal raise Exception("no sync config") 324b2d29daeSVladimir Kotal 325b2d29daeSVladimir Kotal projects = list_projects(logger, uri) 3266100daaaSVladimir Kotal if projects: 327b2d29daeSVladimir Kotal # 328b2d29daeSVladimir Kotal # The driveon=True is needed for the initial indexing of newly 3296100daaaSVladimir Kotal # added project, otherwise the incoming check in the 3306100daaaSVladimir Kotal # opengrok-mirror program would short circuit it. 331b2d29daeSVladimir Kotal # 332b2d29daeSVladimir Kotal if env: 333b2d29daeSVladimir Kotal logger.info('Merging commands with environment') 334b2d29daeSVladimir Kotal commands = merge_commands_env(config["commands"], env) 335b2d29daeSVladimir Kotal logger.debug(config['commands']) 336b2d29daeSVladimir Kotal else: 337b2d29daeSVladimir Kotal commands = config["commands"] 338b2d29daeSVladimir Kotal 339b2d29daeSVladimir Kotal logger.info("Sync starting") 340b2d29daeSVladimir Kotal do_sync(loglevel, commands, config.get('cleanup'), 341b2d29daeSVladimir Kotal projects, config.get("ignore_errors"), uri, 342b2d29daeSVladimir Kotal numworkers, driveon=True, logger=logger, print_output=True) 343b2d29daeSVladimir Kotal logger.info("Sync done") 344b2d29daeSVladimir Kotal 345b2d29daeSVladimir Kotal # Workaround for https://github.com/oracle/opengrok/issues/1670 346b2d29daeSVladimir Kotal Path(os.path.join(OPENGROK_DATA_ROOT, 'timestamp')).touch() 347b2d29daeSVladimir Kotal 348f7d6d77cSVladimir Kotal save_config(logger, uri, config_path) 349b2d29daeSVladimir Kotal 350f1835fddSVladimir Kotal if periodic_sync: 3513d5852a4SVladimir Kotal sleep_seconds = sync_period * 60 352b2d29daeSVladimir Kotal logger.info("Sleeping for {} seconds".format(sleep_seconds)) 353b2d29daeSVladimir Kotal time.sleep(sleep_seconds) 354b531e965SVladimir Kotal elif not periodic_sync: 355b531e965SVladimir Kotal sleep_event.wait() 356b2d29daeSVladimir Kotal 357b2d29daeSVladimir Kotal 358*19f6e235SVladimir Kotaldef create_bare_config(logger, use_projects, extra_indexer_options=None): 359b2d29daeSVladimir Kotal """ 360b2d29daeSVladimir Kotal Create bare configuration file with a few basic settings. 361b2d29daeSVladimir Kotal """ 362b2d29daeSVladimir Kotal 363b2d29daeSVladimir Kotal logger.info('Creating bare configuration in {}'. 364b2d29daeSVladimir Kotal format(OPENGROK_CONFIG_FILE)) 3654810eb96SVladimir Kotal indexer_options = ['-s', OPENGROK_SRC_ROOT, 366b2d29daeSVladimir Kotal '-d', OPENGROK_DATA_ROOT, 367b2d29daeSVladimir Kotal '-c', '/usr/local/bin/ctags', 368b2d29daeSVladimir Kotal '--remote', 'on', 3694810eb96SVladimir Kotal '-H', 370*19f6e235SVladimir Kotal '-S', 371b2d29daeSVladimir Kotal '-W', OPENGROK_CONFIG_FILE, 3724810eb96SVladimir Kotal '--noIndex'] 3734810eb96SVladimir Kotal 3749d6d7c28SVladimir Kotal if extra_indexer_options: 3755b279385SVladimir Kotal if type(extra_indexer_options) is not list: 3765b279385SVladimir Kotal raise Exception("extra_indexer_options has to be a list") 3779d6d7c28SVladimir Kotal indexer_options.extend(extra_indexer_options) 378*19f6e235SVladimir Kotal if use_projects: 379*19f6e235SVladimir Kotal indexer_options.append('-P') 3804810eb96SVladimir Kotal indexer = Indexer(indexer_options, 3814810eb96SVladimir Kotal jar=OPENGROK_JAR, 382b2d29daeSVladimir Kotal logger=logger, doprint=True) 383b2d29daeSVladimir Kotal indexer.execute() 384b2d29daeSVladimir Kotal ret = indexer.getretcode() 385b2d29daeSVladimir Kotal if ret != SUCCESS_EXITVAL: 386b2d29daeSVladimir Kotal logger.error('Command returned {}'.format(ret)) 387b2d29daeSVladimir Kotal logger.error(indexer.geterroutput()) 388b2d29daeSVladimir Kotal raise Exception("Failed to create bare configuration") 389b2d29daeSVladimir Kotal 390b2d29daeSVladimir Kotal 391f1835fddSVladimir Kotaldef get_num_from_env(logger, env_name, default_value): 392f1835fddSVladimir Kotal value = default_value 393f1835fddSVladimir Kotal env_str = os.environ.get(env_name) 394f1835fddSVladimir Kotal if env_str: 395f1835fddSVladimir Kotal try: 396f1835fddSVladimir Kotal n = int(env_str) 397f1835fddSVladimir Kotal if n >= 0: 398f1835fddSVladimir Kotal value = n 399f1835fddSVladimir Kotal except ValueError: 400f1835fddSVladimir Kotal logger.error("{} is not a number: {}". 401f1835fddSVladimir Kotal format(env_name, env_str)) 402f1835fddSVladimir Kotal 403f1835fddSVladimir Kotal return value 404f1835fddSVladimir Kotal 405f1835fddSVladimir Kotal 406f750ae73SVladimir Kotaldef check_index_and_wipe_out(logger): 407f750ae73SVladimir Kotal """ 408f750ae73SVladimir Kotal Check index by running the indexer. If the index does not match 409f750ae73SVladimir Kotal currently running version and the CHECK_INDEX environment variable 410f750ae73SVladimir Kotal is non empty, wipe out the directories under data root. 411f750ae73SVladimir Kotal """ 412f750ae73SVladimir Kotal check_index = os.environ.get('CHECK_INDEX') 413f750ae73SVladimir Kotal if check_index and os.path.exists(OPENGROK_CONFIG_FILE): 414f750ae73SVladimir Kotal logger.info('Checking if index matches current version') 415f750ae73SVladimir Kotal indexer_options = ['-R', OPENGROK_CONFIG_FILE, '--checkIndex'] 416f750ae73SVladimir Kotal indexer = Indexer(indexer_options, logger=logger, 417f750ae73SVladimir Kotal jar=OPENGROK_JAR, doprint=True) 418f750ae73SVladimir Kotal indexer.execute() 419f750ae73SVladimir Kotal if indexer.getretcode() == 1: 420f750ae73SVladimir Kotal logger.info('Wiping out data root') 421f750ae73SVladimir Kotal root = OPENGROK_DATA_ROOT 422f750ae73SVladimir Kotal for entry in os.listdir(root): 423f750ae73SVladimir Kotal path = os.path.join(root, entry) 424f750ae73SVladimir Kotal if os.path.isdir(path): 425f750ae73SVladimir Kotal try: 426f750ae73SVladimir Kotal logger.info("Removing '{}'".format(path)) 427f750ae73SVladimir Kotal shutil.rmtree(path) 428f750ae73SVladimir Kotal except Exception as e: 429f750ae73SVladimir Kotal logger.error("cannot delete '{}': {}".format(path, e)) 430f750ae73SVladimir Kotal 431f750ae73SVladimir Kotal 432f7d6d77cSVladimir Kotaldef main(): 433b2d29daeSVladimir Kotal log_level = os.environ.get('OPENGROK_LOG_LEVEL') 434b2d29daeSVladimir Kotal if log_level: 435b2d29daeSVladimir Kotal log_level = get_log_level(log_level) 436b2d29daeSVladimir Kotal else: 437b2d29daeSVladimir Kotal log_level = logging.INFO 438b2d29daeSVladimir Kotal 439b2d29daeSVladimir Kotal logger = get_console_logger(get_class_basename(), log_level) 440b2d29daeSVladimir Kotal 441f7d6d77cSVladimir Kotal uri, url_root = set_url_root(logger, os.environ.get('URL_ROOT')) 442f7d6d77cSVladimir Kotal logger.debug("URL_ROOT = {}".format(url_root)) 443f7d6d77cSVladimir Kotal logger.debug("URI = {}".format(uri)) 444b2d29daeSVladimir Kotal 44554289af4SVladimir Kotal sync_period = get_num_from_env(logger, 'SYNC_PERIOD_MINUTES', 10) 4463d5852a4SVladimir Kotal if sync_period == 0: 447b531e965SVladimir Kotal logger.info("periodic synchronization disabled") 448b2d29daeSVladimir Kotal else: 4493d5852a4SVladimir Kotal logger.info("synchronization period = {} minutes".format(sync_period)) 450b2d29daeSVladimir Kotal 451b2d29daeSVladimir Kotal # Note that deploy is done before Tomcat is started. 452f7d6d77cSVladimir Kotal deploy(logger, url_root) 453b2d29daeSVladimir Kotal 454f7d6d77cSVladimir Kotal if url_root != '/source': 455f7d6d77cSVladimir Kotal setup_redirect_source(logger, url_root) 456b2d29daeSVladimir Kotal 457b2d29daeSVladimir Kotal env = {} 458dadacd3dSAdam Hornacek extra_indexer_options = os.environ.get('INDEXER_OPT', '') 4594810eb96SVladimir Kotal if extra_indexer_options: 4604810eb96SVladimir Kotal logger.info("extra indexer options: {}".format(extra_indexer_options)) 4614810eb96SVladimir Kotal env['OPENGROK_INDEXER_OPTIONAL_ARGS'] = extra_indexer_options 462b2d29daeSVladimir Kotal if os.environ.get('NOMIRROR'): 463b2d29daeSVladimir Kotal env['OPENGROK_NO_MIRROR'] = os.environ.get('NOMIRROR') 464b2d29daeSVladimir Kotal logger.debug('Extra environment: {}'.format(env)) 465b2d29daeSVladimir Kotal 4664810eb96SVladimir Kotal use_projects = True 4674810eb96SVladimir Kotal if os.environ.get('AVOID_PROJECTS'): 4684810eb96SVladimir Kotal use_projects = False 4694810eb96SVladimir Kotal 470b2d29daeSVladimir Kotal # 471b2d29daeSVladimir Kotal # Create empty configuration to avoid the non existent file exception 472b2d29daeSVladimir Kotal # in the web app during the first web app startup. 473b2d29daeSVladimir Kotal # 474b2d29daeSVladimir Kotal if not os.path.exists(OPENGROK_CONFIG_FILE) or \ 475b2d29daeSVladimir Kotal os.path.getsize(OPENGROK_CONFIG_FILE) == 0: 476*19f6e235SVladimir Kotal create_bare_config(logger, use_projects, extra_indexer_options.split()) 477b2d29daeSVladimir Kotal 478b9361335SVladimir Kotal # 479f750ae73SVladimir Kotal # Index check needs read-only configuration so it is placed 480f750ae73SVladimir Kotal # right after create_bare_config(). 481f750ae73SVladimir Kotal # 482f750ae73SVladimir Kotal check_index_and_wipe_out(logger) 483f750ae73SVladimir Kotal 484f750ae73SVladimir Kotal # 485b9361335SVladimir Kotal # If there is read-only configuration file, merge it with current 486b9361335SVladimir Kotal # configuration. 487b9361335SVladimir Kotal # 488b9361335SVladimir Kotal read_only_config_file = os.environ.get('READONLY_CONFIG_FILE') 489b9361335SVladimir Kotal if read_only_config_file and os.path.exists(read_only_config_file): 490b9361335SVladimir Kotal logger.info('Merging read-only configuration from \'{}\' with current ' 491b9361335SVladimir Kotal 'configuration in \'{}\''.format(read_only_config_file, 492b9361335SVladimir Kotal OPENGROK_CONFIG_FILE)) 493b9361335SVladimir Kotal out_file = None 494b9361335SVladimir Kotal with tempfile.NamedTemporaryFile(mode='w+', delete=False, 495b9361335SVladimir Kotal prefix='merged_config') as tmp_out: 49670e4aafbSVladimir Kotal out_file = tmp_out.name 497b9361335SVladimir Kotal merge_config_files(read_only_config_file, OPENGROK_CONFIG_FILE, 498b9361335SVladimir Kotal tmp_out, jar=OPENGROK_JAR, loglevel=log_level) 499b9361335SVladimir Kotal 500b9361335SVladimir Kotal if out_file and os.path.getsize(out_file) > 0: 501b9361335SVladimir Kotal shutil.move(tmp_out.name, OPENGROK_CONFIG_FILE) 502b9361335SVladimir Kotal else: 503b9361335SVladimir Kotal logger.warning('Failed to merge read-only configuration, ' 504b9361335SVladimir Kotal 'leaving the original in place') 50570e4aafbSVladimir Kotal if out_file: 50670e4aafbSVladimir Kotal os.remove(out_file) 507b9361335SVladimir Kotal 5084810eb96SVladimir Kotal if use_projects: 509f1835fddSVladimir Kotal num_workers = get_num_from_env(logger, 'WORKERS', 510f1835fddSVladimir Kotal multiprocessing.cpu_count()) 511b2d29daeSVladimir Kotal logger.info('Number of sync workers: {}'.format(num_workers)) 512b2d29daeSVladimir Kotal 5134810eb96SVladimir Kotal worker_function = project_syncer 5144810eb96SVladimir Kotal syncer_args = (logger, log_level, uri, 515f7d6d77cSVladimir Kotal OPENGROK_CONFIG_FILE, 5164810eb96SVladimir Kotal sync_period, num_workers, env) 5174810eb96SVladimir Kotal else: 5184810eb96SVladimir Kotal worker_function = indexer_no_projects 5194810eb96SVladimir Kotal syncer_args = (logger, uri, OPENGROK_CONFIG_FILE, sync_period, 5204810eb96SVladimir Kotal extra_indexer_options) 5214810eb96SVladimir Kotal 5224810eb96SVladimir Kotal logger.debug("Starting sync thread") 5236100daaaSVladimir Kotal sync_thread = threading.Thread(target=worker_function, name="Sync thread", 5246100daaaSVladimir Kotal args=syncer_args, daemon=True) 5256100daaaSVladimir Kotal sync_thread.start() 526b2d29daeSVladimir Kotal 527f1835fddSVladimir Kotal rest_port = get_num_from_env(logger, 'REST_PORT', 5000) 528f1835fddSVladimir Kotal token = os.environ.get('REST_TOKEN') 529f1835fddSVladimir Kotal global expected_token 530f1835fddSVladimir Kotal if token: 531f1835fddSVladimir Kotal logger.debug("Setting expected token for REST endpoint" 532f1835fddSVladimir Kotal "on port {} to '{}'".format(rest_port, token)) 533f1835fddSVladimir Kotal expected_token = token 534f1835fddSVladimir Kotal logger.debug("Starting REST thread") 5356100daaaSVladimir Kotal rest_thread = threading.Thread(target=rest_function, name="REST thread", 5366100daaaSVladimir Kotal args=(logger, rest_port), daemon=True) 5376100daaaSVladimir Kotal rest_thread.start() 538f1835fddSVladimir Kotal 539b2d29daeSVladimir Kotal # Start Tomcat last. It will be the foreground process. 540b2d29daeSVladimir Kotal logger.info("Starting Tomcat") 5416100daaaSVladimir Kotal global tomcat_popen 5426100daaaSVladimir Kotal tomcat_popen = subprocess.Popen([os.path.join(tomcat_root, 'bin', 5436100daaaSVladimir Kotal 'catalina.sh'), 5446100daaaSVladimir Kotal 'run']) 5456100daaaSVladimir Kotal tomcat_popen.wait() 546f7d6d77cSVladimir Kotal 547f7d6d77cSVladimir Kotal 548f7d6d77cSVladimir Kotalif __name__ == "__main__": 5496100daaaSVladimir Kotal try: 550f7d6d77cSVladimir Kotal main() 5516100daaaSVladimir Kotal except KeyboardInterrupt: 5526100daaaSVladimir Kotal global tomcat_popen 5536100daaaSVladimir Kotal print("Terminating Tomcat {}".format(tomcat_popen)) 5546100daaaSVladimir Kotal tomcat_popen.terminate() 5556100daaaSVladimir Kotal 5566100daaaSVladimir Kotal sys.exit(1) 557