xref: /OpenGrok/docker/start.py (revision f1835fdd36339b86d4fb74dda273bffe2c75ba8d)
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
29b2d29daeSVladimir Kotalimport threading
30b2d29daeSVladimir Kotalimport time
31d44cbea0SVladimir Kotalfrom pathlib import Path
32d44cbea0SVladimir Kotalfrom requests import get, ConnectionError
33*f1835fddSVladimir Kotalfrom flask import Flask
34*f1835fddSVladimir Kotalfrom flask_httpauth import HTTPTokenAuth
35*f1835fddSVladimir Kotalfrom waitress import serve
36b2d29daeSVladimir Kotal
37b2d29daeSVladimir Kotalfrom opengrok_tools.utils.log import get_console_logger, \
38b2d29daeSVladimir Kotal    get_log_level, get_class_basename
39b2d29daeSVladimir Kotalfrom opengrok_tools.deploy import deploy_war
40b2d29daeSVladimir Kotalfrom opengrok_tools.utils.indexer import Indexer
41b2d29daeSVladimir Kotalfrom opengrok_tools.sync import do_sync
42b2d29daeSVladimir Kotalfrom opengrok_tools.utils.opengrok import list_projects, \
431822b591SVladimir Kotal    add_project, delete_project, get_configuration, set_config_value
44b2d29daeSVladimir Kotalfrom opengrok_tools.utils.readconfig import read_config
45b2d29daeSVladimir Kotalfrom opengrok_tools.utils.exitvals import SUCCESS_EXITVAL
46b2d29daeSVladimir Kotal
47b2d29daeSVladimir Kotal
48b2d29daeSVladimir Kotalfs_root = os.path.abspath('.').split(os.path.sep)[0] + os.path.sep
49b2d29daeSVladimir Kotalif os.environ.get('OPENGROK_TOMCAT_ROOT'):  # debug only
50b2d29daeSVladimir Kotal    tomcat_root = os.environ.get('OPENGROK_TOMCAT_ROOT')
51b2d29daeSVladimir Kotalelse:
52b2d29daeSVladimir Kotal    tomcat_root = os.path.join(fs_root, "usr", "local", "tomcat")
53b2d29daeSVladimir Kotal
54b2d29daeSVladimir Kotalif os.environ.get('OPENGROK_ROOT'):  # debug only
55b2d29daeSVladimir Kotal    OPENGROK_BASE_DIR = os.environ.get('OPENGROK_ROOT')
56b2d29daeSVladimir Kotalelse:
57b2d29daeSVladimir Kotal    OPENGROK_BASE_DIR = os.path.join(fs_root, "opengrok")
58b2d29daeSVladimir Kotal
59b2d29daeSVladimir KotalOPENGROK_LIB_DIR = os.path.join(OPENGROK_BASE_DIR, "lib")
60b2d29daeSVladimir KotalOPENGROK_DATA_ROOT = os.path.join(OPENGROK_BASE_DIR, "data")
61b2d29daeSVladimir KotalOPENGROK_SRC_ROOT = os.path.join(OPENGROK_BASE_DIR, "src")
62b2d29daeSVladimir KotalBODY_INCLUDE_FILE = os.path.join(OPENGROK_DATA_ROOT, "body_include")
63b2d29daeSVladimir KotalOPENGROK_CONFIG_FILE = os.path.join(OPENGROK_BASE_DIR, "etc",
64b2d29daeSVladimir Kotal                                    "configuration.xml")
65b2d29daeSVladimir KotalOPENGROK_WEBAPPS_DIR = os.path.join(tomcat_root, "webapps")
664810eb96SVladimir KotalOPENGROK_JAR = os.path.join(OPENGROK_LIB_DIR, 'opengrok.jar')
67b2d29daeSVladimir Kotal
68*f1835fddSVladimir Kotalexpected_token = None
69*f1835fddSVladimir Kotal
70*f1835fddSVladimir Kotalsleep_event = threading.Event()
71*f1835fddSVladimir Kotalapp = Flask(__name__)
72*f1835fddSVladimir Kotalauth = HTTPTokenAuth(scheme='Bearer')
73*f1835fddSVladimir Kotal
74*f1835fddSVladimir Kotal
75*f1835fddSVladimir Kotal@auth.verify_token
76*f1835fddSVladimir Kotaldef verify_token(token):
77*f1835fddSVladimir Kotal    if expected_token is None:
78*f1835fddSVladimir Kotal        return "yes"
79*f1835fddSVladimir Kotal
80*f1835fddSVladimir Kotal    if token is not None and token == expected_token:
81*f1835fddSVladimir Kotal        return "yes"
82*f1835fddSVladimir Kotal
83*f1835fddSVladimir Kotal
84*f1835fddSVladimir Kotal@app.route('/reindex')
85*f1835fddSVladimir Kotal@auth.login_required
86*f1835fddSVladimir Kotaldef index():
87*f1835fddSVladimir Kotal    # Signal the sync/indexer thread.
88*f1835fddSVladimir Kotal    sleep_event.set()
89*f1835fddSVladimir Kotal    sleep_event.clear()
90*f1835fddSVladimir Kotal
91*f1835fddSVladimir Kotal    return "Reindex triggered"
92*f1835fddSVladimir Kotal
93*f1835fddSVladimir Kotal
94*f1835fddSVladimir Kotaldef rest_function(logger, rest_port):
95*f1835fddSVladimir Kotal    logger.info("Starting REST app on port {}".format(rest_port))
96*f1835fddSVladimir Kotal    serve(app, host="0.0.0.0", port=rest_port)
97*f1835fddSVladimir Kotal
98b2d29daeSVladimir Kotal
99f7d6d77cSVladimir Kotaldef set_url_root(logger, url_root):
100b2d29daeSVladimir Kotal    """
101b2d29daeSVladimir Kotal    Set URL root and URI based on input
102788f14d3SVladimir Kotal    :param logger: logger instance
103b2d29daeSVladimir Kotal    :param url_root: input
104b2d29daeSVladimir Kotal    :return: URI and URL root
105b2d29daeSVladimir Kotal    """
106b2d29daeSVladimir Kotal    if not url_root:
107b2d29daeSVladimir Kotal        url_root = '/'
108b2d29daeSVladimir Kotal
109b2d29daeSVladimir Kotal    if ' ' in url_root:
110b2d29daeSVladimir Kotal        logger.warn('Deployment path contains spaces. Deploying to root')
111b2d29daeSVladimir Kotal        url_root = '/'
112b2d29daeSVladimir Kotal
113b2d29daeSVladimir Kotal    # Remove leading and trailing slashes
114b2d29daeSVladimir Kotal    if url_root.startswith('/'):
115b2d29daeSVladimir Kotal        url_root = url_root[1:]
116b2d29daeSVladimir Kotal    if url_root.endswith('/'):
117b2d29daeSVladimir Kotal        url_root = url_root[:-1]
118b2d29daeSVladimir Kotal
119b2d29daeSVladimir Kotal    uri = "http://localhost:8080/" + url_root
120b2d29daeSVladimir Kotal    #
121b2d29daeSVladimir Kotal    # Make sure URI ends with slash. This is important for the various API
122b2d29daeSVladimir Kotal    # calls, notably for those that check the HTTP error code.
123b2d29daeSVladimir Kotal    # Normally accessing the URI without the terminating slash results in
124b2d29daeSVladimir Kotal    # HTTP redirect (code 302) instead of success (200).
125b2d29daeSVladimir Kotal    #
126b2d29daeSVladimir Kotal    if not uri.endswith('/'):
127b2d29daeSVladimir Kotal        uri = uri + '/'
128b2d29daeSVladimir Kotal
129b2d29daeSVladimir Kotal    return uri, url_root
130b2d29daeSVladimir Kotal
131b2d29daeSVladimir Kotal
132b2d29daeSVladimir Kotaldef get_war_name(url_root):
133d44cbea0SVladimir Kotal    """
134d44cbea0SVladimir Kotal    :param url_root: web app URL root
135d44cbea0SVladimir Kotal    :return: filename of the WAR file
136d44cbea0SVladimir Kotal    """
137b2d29daeSVladimir Kotal    if len(url_root) == 0:
138b2d29daeSVladimir Kotal        return "ROOT.war"
139f7d6d77cSVladimir Kotal
140b2d29daeSVladimir Kotal    return url_root + ".war"
141b2d29daeSVladimir Kotal
142b2d29daeSVladimir Kotal
143f7d6d77cSVladimir Kotaldef deploy(logger, url_root):
144b2d29daeSVladimir Kotal    """
145b2d29daeSVladimir Kotal    Deploy the web application
146788f14d3SVladimir Kotal    :param logger: logger instance
147b2d29daeSVladimir Kotal    :param url_root: web app URL root
148b2d29daeSVladimir Kotal    """
149b2d29daeSVladimir Kotal
150b2d29daeSVladimir Kotal    logger.info('Deploying web application')
151b2d29daeSVladimir Kotal    webapps_dir = os.path.join(tomcat_root, 'webapps')
152b2d29daeSVladimir Kotal    if not os.path.isdir(webapps_dir):
153b2d29daeSVladimir Kotal        raise Exception("{} is not a directory".format(webapps_dir))
154b2d29daeSVladimir Kotal
155b2d29daeSVladimir Kotal    for item in os.listdir(webapps_dir):
156b2d29daeSVladimir Kotal        subdir = os.path.join(webapps_dir, item)
157b2d29daeSVladimir Kotal        if os.path.isdir(subdir):
158b2d29daeSVladimir Kotal            logger.debug("Removing '{}' directory recursively".format(subdir))
159b2d29daeSVladimir Kotal            shutil.rmtree(subdir)
160b2d29daeSVladimir Kotal
161b2d29daeSVladimir Kotal    deploy_war(logger, os.path.join(OPENGROK_LIB_DIR, "source.war"),
162b2d29daeSVladimir Kotal               os.path.join(OPENGROK_WEBAPPS_DIR, get_war_name(url_root)),
163b2d29daeSVladimir Kotal               OPENGROK_CONFIG_FILE, None)
164b2d29daeSVladimir Kotal
165b2d29daeSVladimir Kotal
166f7d6d77cSVladimir Kotaldef setup_redirect_source(logger, url_root):
167b2d29daeSVladimir Kotal    """
168b2d29daeSVladimir Kotal    Set up redirect from /source
169b2d29daeSVladimir Kotal    """
170b2d29daeSVladimir Kotal    logger.debug("Setting up redirect from /source to '{}'".format(url_root))
171b2d29daeSVladimir Kotal    source_dir = os.path.join(OPENGROK_WEBAPPS_DIR, "source")
172b2d29daeSVladimir Kotal    if not os.path.isdir(source_dir):
173b2d29daeSVladimir Kotal        os.makedirs(source_dir)
174b2d29daeSVladimir Kotal
175b2d29daeSVladimir Kotal    with open(os.path.join(source_dir, "index.jsp"), "w+") as index:
176b2d29daeSVladimir Kotal        index.write("<% response.sendRedirect(\"/{}\"); %>".format(url_root))
177b2d29daeSVladimir Kotal
178b2d29daeSVladimir Kotal
179f7d6d77cSVladimir Kotaldef wait_for_tomcat(logger, uri):
180b2d29daeSVladimir Kotal    """
181b2d29daeSVladimir Kotal    Active/busy waiting for Tomcat to come up.
182b2d29daeSVladimir Kotal    Currently there is no upper time bound.
183b2d29daeSVladimir Kotal    """
184b2d29daeSVladimir Kotal    logger.info("Waiting for Tomcat to start")
185b2d29daeSVladimir Kotal
186b2d29daeSVladimir Kotal    while True:
187b2d29daeSVladimir Kotal        try:
188d44cbea0SVladimir Kotal            ret = get(uri)
189d44cbea0SVladimir Kotal            status = ret.status_code
190b2d29daeSVladimir Kotal        except ConnectionError:
191b2d29daeSVladimir Kotal            status = 0
192b2d29daeSVladimir Kotal
193b2d29daeSVladimir Kotal        if status != 200:
194b2d29daeSVladimir Kotal            logger.debug("Got status {} for {}, sleeping for 1 second".
195b2d29daeSVladimir Kotal                         format(status, uri))
196b2d29daeSVladimir Kotal            time.sleep(1)
197b2d29daeSVladimir Kotal        else:
198b2d29daeSVladimir Kotal            break
199b2d29daeSVladimir Kotal
200b2d29daeSVladimir Kotal    logger.info("Tomcat is ready")
201b2d29daeSVladimir Kotal
202b2d29daeSVladimir Kotal
203f7d6d77cSVladimir Kotaldef refresh_projects(logger, uri):
204b2d29daeSVladimir Kotal    """
205b2d29daeSVladimir Kotal    Ensure each immediate source root subdirectory is a project.
206b2d29daeSVladimir Kotal    """
207b2d29daeSVladimir Kotal    webapp_projects = list_projects(logger, uri)
208b2d29daeSVladimir Kotal    logger.debug('Projects from the web app: {}'.format(webapp_projects))
209b2d29daeSVladimir Kotal    src_root = OPENGROK_SRC_ROOT
210b2d29daeSVladimir Kotal
211b2d29daeSVladimir Kotal    # Add projects.
212b2d29daeSVladimir Kotal    for item in os.listdir(src_root):
213b2d29daeSVladimir Kotal        logger.debug('Got item {}'.format(item))
214b2d29daeSVladimir Kotal        if os.path.isdir(os.path.join(src_root, item)):
215b2d29daeSVladimir Kotal            if item not in webapp_projects:
216b2d29daeSVladimir Kotal                logger.info("Adding project {}".format(item))
217b2d29daeSVladimir Kotal                add_project(logger, item, uri)
218b2d29daeSVladimir Kotal
219b2d29daeSVladimir Kotal    # Remove projects
220b2d29daeSVladimir Kotal    for item in webapp_projects:
221b2d29daeSVladimir Kotal        if not os.path.isdir(os.path.join(src_root, item)):
222b2d29daeSVladimir Kotal            logger.info("Deleting project {}".format(item))
223b2d29daeSVladimir Kotal            delete_project(logger, item, uri)
224b2d29daeSVladimir Kotal
225b2d29daeSVladimir Kotal
226f7d6d77cSVladimir Kotaldef save_config(logger, uri, config_path):
227b2d29daeSVladimir Kotal    """
228b2d29daeSVladimir Kotal    Retrieve configuration from the web app and write it to file.
229788f14d3SVladimir Kotal    :param logger: logger instance
230b2d29daeSVladimir Kotal    :param uri: web app URI
231b2d29daeSVladimir Kotal    :param config_path: file path
232b2d29daeSVladimir Kotal    """
233b2d29daeSVladimir Kotal
234b2d29daeSVladimir Kotal    logger.info('Saving configuration to {}'.format(config_path))
235b2d29daeSVladimir Kotal    config = get_configuration(logger, uri)
236b2d29daeSVladimir Kotal    with open(config_path, "w+") as config_file:
237b2d29daeSVladimir Kotal        config_file.write(config)
238b2d29daeSVladimir Kotal
239b2d29daeSVladimir Kotal
240b2d29daeSVladimir Kotaldef merge_commands_env(commands, env):
241b2d29daeSVladimir Kotal    """
242b2d29daeSVladimir Kotal    Merge environment into command structure. If any of the commands has
243b2d29daeSVladimir Kotal    an environment already set, the env is merged in.
244b2d29daeSVladimir Kotal    :param commands: commands structure
245b2d29daeSVladimir Kotal    :param env: environment dictionary
246b2d29daeSVladimir Kotal    :return: updated commands structure
247b2d29daeSVladimir Kotal    """
248b2d29daeSVladimir Kotal    for cmd in commands:
249b2d29daeSVladimir Kotal        cmd_env = cmd.get('env')
250b2d29daeSVladimir Kotal        if cmd_env:
251b2d29daeSVladimir Kotal            cmd.env.update(env)
252b2d29daeSVladimir Kotal        else:
253b2d29daeSVladimir Kotal            cmd['env'] = env
254b2d29daeSVladimir Kotal
255b2d29daeSVladimir Kotal    return commands
256b2d29daeSVladimir Kotal
257b2d29daeSVladimir Kotal
2584810eb96SVladimir Kotaldef indexer_no_projects(logger, uri, config_path, sync_period,
2594810eb96SVladimir Kotal                        extra_indexer_options):
2604810eb96SVladimir Kotal    """
2614810eb96SVladimir Kotal    Project less indexer
2624810eb96SVladimir Kotal    """
2634810eb96SVladimir Kotal
2644810eb96SVladimir Kotal    wait_for_tomcat(logger, uri)
2654810eb96SVladimir Kotal
266*f1835fddSVladimir Kotal    periodic_sync = True
267*f1835fddSVladimir Kotal    if sync_period is None or sync_period == 0:
268*f1835fddSVladimir Kotal        periodic_sync = False
269*f1835fddSVladimir Kotal
2704810eb96SVladimir Kotal    while True:
271*f1835fddSVladimir Kotal        if not periodic_sync:
272*f1835fddSVladimir Kotal            sleep_event.wait()
273*f1835fddSVladimir Kotal
2744810eb96SVladimir Kotal        indexer_options = ['-s', OPENGROK_SRC_ROOT,
2754810eb96SVladimir Kotal                           '-d', OPENGROK_DATA_ROOT,
2764810eb96SVladimir Kotal                           '-c', '/usr/local/bin/ctags',
2774810eb96SVladimir Kotal                           '--remote', 'on',
2784810eb96SVladimir Kotal                           '-H',
2794810eb96SVladimir Kotal                           '-W', config_path,
2804810eb96SVladimir Kotal                           '-U', uri]
2814810eb96SVladimir Kotal        if extra_indexer_options:
2824810eb96SVladimir Kotal            logger.debug("Adding extra indexer options: {}".
2834810eb96SVladimir Kotal                         format(extra_indexer_options))
2844810eb96SVladimir Kotal            indexer_options.extend(extra_indexer_options.split())
2854810eb96SVladimir Kotal        indexer = Indexer(indexer_options, logger=logger,
286d2f093daSVladimir Kotal                          jar=OPENGROK_JAR, doprint=True)
2874810eb96SVladimir Kotal        indexer.execute()
2884810eb96SVladimir Kotal
289*f1835fddSVladimir Kotal        if periodic_sync:
2904810eb96SVladimir Kotal            sleep_seconds = sync_period * 60
2914810eb96SVladimir Kotal            logger.info("Sleeping for {} seconds".format(sleep_seconds))
2924810eb96SVladimir Kotal            time.sleep(sleep_seconds)
2934810eb96SVladimir Kotal
2944810eb96SVladimir Kotal
2954810eb96SVladimir Kotaldef project_syncer(logger, loglevel, uri, config_path, sync_period,
2964810eb96SVladimir Kotal                   numworkers, env):
297b2d29daeSVladimir Kotal    """
298b2d29daeSVladimir Kotal    Wrapper for running opengrok-sync.
299b2d29daeSVladimir Kotal    To be run in a thread/process in the background.
300b2d29daeSVladimir Kotal    """
301b2d29daeSVladimir Kotal
302f7d6d77cSVladimir Kotal    wait_for_tomcat(logger, uri)
303b2d29daeSVladimir Kotal
3041822b591SVladimir Kotal    set_config_value(logger, 'projectsEnabled', 'true', uri)
3051822b591SVladimir Kotal
306*f1835fddSVladimir Kotal    periodic_sync = True
307*f1835fddSVladimir Kotal    if sync_period is None or sync_period == 0:
308*f1835fddSVladimir Kotal        periodic_sync = False
309*f1835fddSVladimir Kotal
310b2d29daeSVladimir Kotal    while True:
311*f1835fddSVladimir Kotal        if not periodic_sync:
312*f1835fddSVladimir Kotal            sleep_event.wait()
313*f1835fddSVladimir Kotal
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)
326b2d29daeSVladimir Kotal        #
327b2d29daeSVladimir Kotal        # The driveon=True is needed for the initial indexing of newly
328b2d29daeSVladimir Kotal        # added project, otherwise the incoming check in the opengrok-mirror
3293d5852a4SVladimir Kotal        # program would short circuit it.
330b2d29daeSVladimir Kotal        #
331b2d29daeSVladimir Kotal        if env:
332b2d29daeSVladimir Kotal            logger.info('Merging commands with environment')
333b2d29daeSVladimir Kotal            commands = merge_commands_env(config["commands"], env)
334b2d29daeSVladimir Kotal            logger.debug(config['commands'])
335b2d29daeSVladimir Kotal        else:
336b2d29daeSVladimir Kotal            commands = config["commands"]
337b2d29daeSVladimir Kotal
338b2d29daeSVladimir Kotal        logger.info("Sync starting")
339b2d29daeSVladimir Kotal        do_sync(loglevel, commands, config.get('cleanup'),
340b2d29daeSVladimir Kotal                projects, config.get("ignore_errors"), uri,
341b2d29daeSVladimir Kotal                numworkers, driveon=True, logger=logger, print_output=True)
342b2d29daeSVladimir Kotal        logger.info("Sync done")
343b2d29daeSVladimir Kotal
344b2d29daeSVladimir Kotal        # Workaround for https://github.com/oracle/opengrok/issues/1670
345b2d29daeSVladimir Kotal        Path(os.path.join(OPENGROK_DATA_ROOT, 'timestamp')).touch()
346b2d29daeSVladimir Kotal
347f7d6d77cSVladimir Kotal        save_config(logger, uri, config_path)
348b2d29daeSVladimir Kotal
349*f1835fddSVladimir Kotal        if periodic_sync:
3503d5852a4SVladimir Kotal            sleep_seconds = sync_period * 60
351b2d29daeSVladimir Kotal            logger.info("Sleeping for {} seconds".format(sleep_seconds))
352b2d29daeSVladimir Kotal            time.sleep(sleep_seconds)
353b2d29daeSVladimir Kotal
354b2d29daeSVladimir Kotal
3559d6d7c28SVladimir Kotaldef create_bare_config(logger, extra_indexer_options=None):
356b2d29daeSVladimir Kotal    """
357b2d29daeSVladimir Kotal    Create bare configuration file with a few basic settings.
358b2d29daeSVladimir Kotal    """
359b2d29daeSVladimir Kotal
360b2d29daeSVladimir Kotal    logger.info('Creating bare configuration in {}'.
361b2d29daeSVladimir Kotal                format(OPENGROK_CONFIG_FILE))
3624810eb96SVladimir Kotal    indexer_options = ['-s', OPENGROK_SRC_ROOT,
363b2d29daeSVladimir Kotal                       '-d', OPENGROK_DATA_ROOT,
364b2d29daeSVladimir Kotal                       '-c', '/usr/local/bin/ctags',
365b2d29daeSVladimir Kotal                       '--remote', 'on',
3664810eb96SVladimir Kotal                       '-H',
367b2d29daeSVladimir Kotal                       '-W', OPENGROK_CONFIG_FILE,
3684810eb96SVladimir Kotal                       '--noIndex']
3694810eb96SVladimir Kotal
3709d6d7c28SVladimir Kotal    if extra_indexer_options:
3715b279385SVladimir Kotal        if type(extra_indexer_options) is not list:
3725b279385SVladimir Kotal            raise Exception("extra_indexer_options has to be a list")
3739d6d7c28SVladimir Kotal        indexer_options.extend(extra_indexer_options)
3744810eb96SVladimir Kotal    indexer = Indexer(indexer_options,
3754810eb96SVladimir Kotal                      jar=OPENGROK_JAR,
376b2d29daeSVladimir Kotal                      logger=logger, doprint=True)
377b2d29daeSVladimir Kotal    indexer.execute()
378b2d29daeSVladimir Kotal    ret = indexer.getretcode()
379b2d29daeSVladimir Kotal    if ret != SUCCESS_EXITVAL:
380b2d29daeSVladimir Kotal        logger.error('Command returned {}'.format(ret))
381b2d29daeSVladimir Kotal        logger.error(indexer.geterroutput())
382b2d29daeSVladimir Kotal        raise Exception("Failed to create bare configuration")
383b2d29daeSVladimir Kotal
384b2d29daeSVladimir Kotal
385*f1835fddSVladimir Kotaldef get_num_from_env(logger, env_name, default_value):
386*f1835fddSVladimir Kotal    value = default_value
387*f1835fddSVladimir Kotal    env_str = os.environ.get(env_name)
388*f1835fddSVladimir Kotal    if env_str:
389*f1835fddSVladimir Kotal        try:
390*f1835fddSVladimir Kotal            n = int(env_str)
391*f1835fddSVladimir Kotal            if n >= 0:
392*f1835fddSVladimir Kotal                value = n
393*f1835fddSVladimir Kotal        except ValueError:
394*f1835fddSVladimir Kotal            logger.error("{} is not a number: {}".
395*f1835fddSVladimir Kotal                         format(env_name, env_str))
396*f1835fddSVladimir Kotal
397*f1835fddSVladimir Kotal    return value
398*f1835fddSVladimir Kotal
399*f1835fddSVladimir Kotal
400f7d6d77cSVladimir Kotaldef main():
401b2d29daeSVladimir Kotal    log_level = os.environ.get('OPENGROK_LOG_LEVEL')
402b2d29daeSVladimir Kotal    if log_level:
403b2d29daeSVladimir Kotal        log_level = get_log_level(log_level)
404b2d29daeSVladimir Kotal    else:
405b2d29daeSVladimir Kotal        log_level = logging.INFO
406b2d29daeSVladimir Kotal
407b2d29daeSVladimir Kotal    logger = get_console_logger(get_class_basename(), log_level)
408b2d29daeSVladimir Kotal
409f7d6d77cSVladimir Kotal    uri, url_root = set_url_root(logger, os.environ.get('URL_ROOT'))
410f7d6d77cSVladimir Kotal    logger.debug("URL_ROOT = {}".format(url_root))
411f7d6d77cSVladimir Kotal    logger.debug("URI = {}".format(uri))
412b2d29daeSVladimir Kotal
4133d5852a4SVladimir Kotal    # default period for syncing (in minutes)
414*f1835fddSVladimir Kotal    sync_period = get_num_from_env(logger, 'SYNC_TIME_MINUTES', 10)
4153d5852a4SVladimir Kotal    if sync_period == 0:
4163d5852a4SVladimir Kotal        logger.info("synchronization disabled")
417b2d29daeSVladimir Kotal    else:
4183d5852a4SVladimir Kotal        logger.info("synchronization period = {} minutes".format(sync_period))
419b2d29daeSVladimir Kotal
420b2d29daeSVladimir Kotal    # Note that deploy is done before Tomcat is started.
421f7d6d77cSVladimir Kotal    deploy(logger, url_root)
422b2d29daeSVladimir Kotal
423f7d6d77cSVladimir Kotal    if url_root != '/source':
424f7d6d77cSVladimir Kotal        setup_redirect_source(logger, url_root)
425b2d29daeSVladimir Kotal
426b2d29daeSVladimir Kotal    env = {}
427dadacd3dSAdam Hornacek    extra_indexer_options = os.environ.get('INDEXER_OPT', '')
4284810eb96SVladimir Kotal    if extra_indexer_options:
4294810eb96SVladimir Kotal        logger.info("extra indexer options: {}".format(extra_indexer_options))
4304810eb96SVladimir Kotal        env['OPENGROK_INDEXER_OPTIONAL_ARGS'] = extra_indexer_options
431b2d29daeSVladimir Kotal    if os.environ.get('NOMIRROR'):
432b2d29daeSVladimir Kotal        env['OPENGROK_NO_MIRROR'] = os.environ.get('NOMIRROR')
433b2d29daeSVladimir Kotal    logger.debug('Extra environment: {}'.format(env))
434b2d29daeSVladimir Kotal
4354810eb96SVladimir Kotal    use_projects = True
4364810eb96SVladimir Kotal    if os.environ.get('AVOID_PROJECTS'):
4374810eb96SVladimir Kotal        use_projects = False
4384810eb96SVladimir Kotal
439b2d29daeSVladimir Kotal    #
440b2d29daeSVladimir Kotal    # Create empty configuration to avoid the non existent file exception
441b2d29daeSVladimir Kotal    # in the web app during the first web app startup.
442b2d29daeSVladimir Kotal    #
443b2d29daeSVladimir Kotal    if not os.path.exists(OPENGROK_CONFIG_FILE) or \
444b2d29daeSVladimir Kotal            os.path.getsize(OPENGROK_CONFIG_FILE) == 0:
445c6f6c3d1SVladimir Kotal        create_bare_config(logger, extra_indexer_options.split())
446b2d29daeSVladimir Kotal
4474810eb96SVladimir Kotal    if use_projects:
448*f1835fddSVladimir Kotal        num_workers = get_num_from_env(logger, 'WORKERS',
449*f1835fddSVladimir Kotal                                       multiprocessing.cpu_count())
450b2d29daeSVladimir Kotal        logger.info('Number of sync workers: {}'.format(num_workers))
451b2d29daeSVladimir Kotal
4524810eb96SVladimir Kotal        worker_function = project_syncer
4534810eb96SVladimir Kotal        syncer_args = (logger, log_level, uri,
454f7d6d77cSVladimir Kotal                       OPENGROK_CONFIG_FILE,
4554810eb96SVladimir Kotal                       sync_period, num_workers, env)
4564810eb96SVladimir Kotal    else:
4574810eb96SVladimir Kotal        worker_function = indexer_no_projects
4584810eb96SVladimir Kotal        syncer_args = (logger, uri, OPENGROK_CONFIG_FILE, sync_period,
4594810eb96SVladimir Kotal                       extra_indexer_options)
4604810eb96SVladimir Kotal
4614810eb96SVladimir Kotal    logger.debug("Starting sync thread")
4624810eb96SVladimir Kotal    thread = threading.Thread(target=worker_function, name="Sync thread",
4634810eb96SVladimir Kotal                              args=syncer_args)
464f7d6d77cSVladimir Kotal    thread.start()
465b2d29daeSVladimir Kotal
466*f1835fddSVladimir Kotal    rest_port = get_num_from_env(logger, 'REST_PORT', 5000)
467*f1835fddSVladimir Kotal    token = os.environ.get('REST_TOKEN')
468*f1835fddSVladimir Kotal    global expected_token
469*f1835fddSVladimir Kotal    if token:
470*f1835fddSVladimir Kotal        logger.debug("Setting expected token for REST endpoint"
471*f1835fddSVladimir Kotal                     "on port {} to '{}'".format(rest_port, token))
472*f1835fddSVladimir Kotal        expected_token = token
473*f1835fddSVladimir Kotal    logger.debug("Starting REST thread")
474*f1835fddSVladimir Kotal    thread = threading.Thread(target=rest_function, name="REST thread",
475*f1835fddSVladimir Kotal                              args=(logger, rest_port))
476*f1835fddSVladimir Kotal    thread.start()
477*f1835fddSVladimir Kotal
478b2d29daeSVladimir Kotal    # Start Tomcat last. It will be the foreground process.
479b2d29daeSVladimir Kotal    logger.info("Starting Tomcat")
480b2d29daeSVladimir Kotal    subprocess.run([os.path.join(tomcat_root, 'bin', 'catalina.sh'), 'run'])
481f7d6d77cSVladimir Kotal
482f7d6d77cSVladimir Kotal
483f7d6d77cSVladimir Kotalif __name__ == "__main__":
484f7d6d77cSVladimir Kotal    main()
485