From d9fa0659ffafa84581e110ad2cf7599f33d12458 Mon Sep 17 00:00:00 2001 From: Rik Veenboer Date: Fri, 22 Nov 2024 09:52:06 +0100 Subject: [PATCH] snapshot docker files for seafile --- seafile/docker/Dockerfile | 50 +++ seafile/docker/db_update_helper.py | 381 ++++++++++++++++++++ seafile/docker/seafile-entrypoint.sh | 501 +++++++++++++++++++++++++++ 3 files changed, 932 insertions(+) create mode 100644 seafile/docker/Dockerfile create mode 100644 seafile/docker/db_update_helper.py create mode 100755 seafile/docker/seafile-entrypoint.sh diff --git a/seafile/docker/Dockerfile b/seafile/docker/Dockerfile new file mode 100644 index 0000000..e56cb07 --- /dev/null +++ b/seafile/docker/Dockerfile @@ -0,0 +1,50 @@ +# https://github.com/Gronis/docker-seafile/blob/master/Dockerfile + +FROM --platform=linux/amd64 debian:bookworm-slim +MAINTAINER Robin Grönerg + +ENV VERSION=11.0.8 +ENV DOCKERIZE_VERSION v0.6.1 + +RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \ + procps python3 python3-dev python3-setuptools python3-pip \ + python3-wheel curl sqlite3 default-libmysqlclient-dev \ + build-essential autoconf libtool pkg-config \ + libffi-dev libjpeg-dev zlib1g-dev + +RUN pip3 install --break-system-packages --timeout=3600 \ + pylibmc jinja2 "sqlalchemy<2" python3-ldap \ + django-pylibmc lxml \ + future mysqlclient \ + Pillow captcha \ + django_simple_captcha \ + djangosaml2 pysaml2 \ + pycryptodome cffi + +RUN apt-get purge -y \ + python3-dev python3-setuptools python3-pip python3-wheel \ + build-essential autoconf libtool pkg-config + +RUN apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /root/.cache /usr/share/doc/* && \ + find / -type f -name '*.py[co]' -delete -or -type d -name '__pycache__' -delete + +RUN curl -L https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz | tar -xz -C /usr/local/bin + +RUN useradd -d /seafile -M -s /bin/bash -c "Seafile User" seafile + +RUN mkdir -p /opt/haiwen /seafile/ + +RUN curl -sL $(curl -sL https://www.seafile.com/en/download/ \ + | grep -oE 'https://.*seafile-server.*x86-64.tar.gz' \ + | sed -e "s/[0-9]+\.[0-9]+\.[0-9]+/$VERSION/g" | grep $VERSION \ + | sort -r | head -1) \ + | tar -C /opt/haiwen/ -xz + +RUN chown -R seafile:seafile /seafile /opt/haiwen +RUN ln -s /usr/bin/python3 /usr/bin/python +COPY ["seafile-entrypoint.sh", "/usr/local/bin/"] + +EXPOSE 8000 8082 + +ENTRYPOINT ["/usr/local/bin/seafile-entrypoint.sh"] diff --git a/seafile/docker/db_update_helper.py b/seafile/docker/db_update_helper.py new file mode 100644 index 0000000..90947ee --- /dev/null +++ b/seafile/docker/db_update_helper.py @@ -0,0 +1,381 @@ +# coding: UTF-8 + +import sys +import os +import configparser +import glob + +HAS_PYMYSQL = True +try: + import pymysql +except ImportError: + HAS_PYMYSQL = False + +HAS_SQLITE3 = True +try: + import sqlite3 +except ImportError: + HAS_SQLITE3 = False + +class EnvManager(object): + def __init__(self): + self.upgrade_dir = os.path.dirname(__file__) + self.install_path = os.path.dirname(self.upgrade_dir) + self.top_dir = os.path.dirname(self.install_path) + self.ccnet_dir = os.environ['CCNET_CONF_DIR'] + self.seafile_dir = os.environ['SEAFILE_CONF_DIR'] + self.central_config_dir = os.environ.get('SEAFILE_CENTRAL_CONF_DIR') + self.seafevents_db_dir = os.path.join(os.path.dirname(self.install_path), 'pro-data') + + +env_mgr = EnvManager() + + +class Utils(object): + @staticmethod + def highlight(content, is_error=False): + '''Add ANSI color to content to get it highlighted on terminal''' + if is_error: + return '\x1b[1;31m%s\x1b[m' % content + else: + return '\x1b[1;32m%s\x1b[m' % content + + @staticmethod + def info(msg): + print(Utils.highlight('[INFO] ') + msg) + + @staticmethod + def warning(msg): + print(Utils.highlight('[WARNING] ') + msg) + + @staticmethod + def error(msg): + print(Utils.highlight('[ERROR] ') + msg) + sys.exit(1) + + @staticmethod + def read_config(config_path, defaults): + if not os.path.exists(config_path): + Utils.error('Config path %s doesn\'t exist, stop db upgrade' % + config_path) + cp = configparser.ConfigParser(defaults) + cp.read(config_path) + return cp + + +class MySQLDBInfo(object): + def __init__(self, host, port, username, password, db, unix_socket=None): + self.host = host + self.port = port + self.username = username + self.password = password + self.db = db + self.unix_socket = unix_socket + + +class DBUpdater(object): + def __init__(self, version, name): + self.sql_dir = os.path.join(env_mgr.upgrade_dir, 'sql', version, name) + pro_path = os.path.join(env_mgr.install_path, 'pro') + self.is_pro = os.path.exists(pro_path) + self.version = version + + @staticmethod + def get_instance(version): + '''Detect whether we are using mysql or sqlite3''' + ccnet_db_info = DBUpdater.get_ccnet_mysql_info(version) + seafile_db_info = DBUpdater.get_seafile_mysql_info(version) + seahub_db_info = DBUpdater.get_seahub_mysql_info() + + if ccnet_db_info and seafile_db_info and seahub_db_info: + Utils.info('You are using MySQL') + if not HAS_PYMYSQL: + Utils.error('Python pymysql module is not found') + updater = MySQLDBUpdater(version, ccnet_db_info, seafile_db_info, seahub_db_info) + + elif (ccnet_db_info is None) and (seafile_db_info is None) and (seahub_db_info is None): + Utils.info('You are using SQLite3') + if not HAS_SQLITE3: + Utils.error('Python sqlite3 module is not found') + updater = SQLiteDBUpdater(version) + + else: + def to_db_string(info): + if info is None: + return 'SQLite3' + else: + return 'MySQL' + Utils.error('Error:\n ccnet is using %s\n seafile is using %s\n seahub is using %s\n' + % (to_db_string(ccnet_db_info), + to_db_string(seafile_db_info), + to_db_string(seahub_db_info))) + + return updater + + def update_db(self): + ccnet_sql = os.path.join(self.sql_dir, 'ccnet.sql') + seafile_sql = os.path.join(self.sql_dir, 'seafile.sql') + seahub_sql = os.path.join(self.sql_dir, 'seahub.sql') + seafevents_sql = os.path.join(self.sql_dir, 'seafevents.sql') + + if os.path.exists(ccnet_sql): + Utils.info('updating ccnet database...') + self.update_ccnet_sql(ccnet_sql) + + if os.path.exists(seafile_sql): + Utils.info('updating seafile database...') + self.update_seafile_sql(seafile_sql) + + if os.path.exists(seahub_sql): + Utils.info('updating seahub database...') + self.update_seahub_sql(seahub_sql) + + if os.path.exists(seafevents_sql): + self.update_seafevents_sql(seafevents_sql) + + @staticmethod + def get_ccnet_mysql_info(version): + config_path = env_mgr.central_config_dir + ccnet_conf = os.path.join(config_path, 'ccnet.conf') + defaults = { + 'HOST': '127.0.0.1', + 'PORT': '3306', + 'UNIX_SOCKET': '', + } + + config = Utils.read_config(ccnet_conf, defaults) + db_section = 'Database' + + if not config.has_section(db_section): + return None + + type = config.get(db_section, 'ENGINE') + if type != 'mysql': + return None + + try: + host = config.get(db_section, 'HOST') + port = config.getint(db_section, 'PORT') + username = config.get(db_section, 'USER') + password = config.get(db_section, 'PASSWD') + db = config.get(db_section, 'DB') + unix_socket = config.get(db_section, 'UNIX_SOCKET') + except configparser.NoOptionError as e: + Utils.error('Database config in ccnet.conf is invalid: %s' % e) + + info = MySQLDBInfo(host, port, username, password, db, unix_socket) + return info + + @staticmethod + def get_seafile_mysql_info(version): + config_path = env_mgr.central_config_dir + seafile_conf = os.path.join(config_path, 'seafile.conf') + defaults = { + 'HOST': '127.0.0.1', + 'PORT': '3306', + 'UNIX_SOCKET': '', + } + config = Utils.read_config(seafile_conf, defaults) + db_section = 'database' + + if not config.has_section(db_section): + return None + + type = config.get(db_section, 'type') + if type != 'mysql': + return None + + try: + host = config.get(db_section, 'host') + port = config.getint(db_section, 'port') + username = config.get(db_section, 'user') + password = config.get(db_section, 'password') + db = config.get(db_section, 'db_name') + unix_socket = config.get(db_section, 'unix_socket') + except configparser.NoOptionError as e: + Utils.error('Database config in seafile.conf is invalid: %s' % e) + + info = MySQLDBInfo(host, port, username, password, db, unix_socket) + return info + + @staticmethod + def get_seahub_mysql_info(): + sys.path.insert(0, env_mgr.top_dir) + if env_mgr.central_config_dir: + sys.path.insert(0, env_mgr.central_config_dir) + try: + import seahub_settings # pylint: disable=F0401 + except ImportError as e: + Utils.error('Failed to import seahub_settings.py: %s' % e) + + if not hasattr(seahub_settings, 'DATABASES'): + return None + + try: + d = seahub_settings.DATABASES['default'] + if d['ENGINE'] != 'django.db.backends.mysql': + return None + + host = d.get('HOST', '127.0.0.1') + port = int(d.get('PORT', 3306)) + username = d['USER'] + password = d['PASSWORD'] + db = d['NAME'] + unix_socket = host if host.startswith('/') else None + except KeyError: + Utils.error('Database config in seahub_settings.py is invalid: %s' % e) + + info = MySQLDBInfo(host, port, username, password, db, unix_socket) + return info + + def update_ccnet_sql(self, ccnet_sql): + raise NotImplementedError + + def update_seafile_sql(self, seafile_sql): + raise NotImplementedError + + def update_seahub_sql(self, seahub_sql): + raise NotImplementedError + + def update_seafevents_sql(self, seafevents_sql): + raise NotImplementedError + +class CcnetSQLiteDB(object): + def __init__(self, ccnet_dir): + self.ccnet_dir = ccnet_dir + + def get_db(self, dbname): + dbs = ( + 'ccnet.db', + 'GroupMgr/groupmgr.db', + 'misc/config.db', + 'OrgMgr/orgmgr.db', + 'PeerMgr/usermgr.db', + ) + for db in dbs: + if os.path.splitext(os.path.basename(db))[0] == dbname: + return os.path.join(self.ccnet_dir, db) + +class SQLiteDBUpdater(DBUpdater): + def __init__(self, version): + DBUpdater.__init__(self, version, 'sqlite3') + + self.ccnet_db = CcnetSQLiteDB(env_mgr.ccnet_dir) + self.seafile_db = os.path.join(env_mgr.seafile_dir, 'seafile.db') + self.seahub_db = os.path.join(env_mgr.top_dir, 'seahub.db') + self.seafevents_db = os.path.join(env_mgr.seafevents_db_dir, 'seafevents.db') + + def update_db(self): + super(SQLiteDBUpdater, self).update_db() + for sql_path in glob.glob(os.path.join(self.sql_dir, 'ccnet', '*.sql')): + self.update_ccnet_sql(sql_path) + + def apply_sqls(self, db_path, sql_path): + with open(sql_path, 'r') as fp: + lines = fp.read().split(';') + + with sqlite3.connect(db_path) as conn: + for line in lines: + line = line.strip() + if not line: + continue + else: + try: + conn.execute(line) + except Exception as e: + print(e) + + def update_ccnet_sql(self, sql_path): + dbname = os.path.splitext(os.path.basename(sql_path))[0] + self.apply_sqls(self.ccnet_db.get_db(dbname), sql_path) + + def update_seafile_sql(self, sql_path): + self.apply_sqls(self.seafile_db, sql_path) + + def update_seahub_sql(self, sql_path): + self.apply_sqls(self.seahub_db, sql_path) + + def update_seafevents_sql(self, sql_path): + if self.is_pro: + Utils.info('seafevents do not support sqlite3 database') + + +class MySQLDBUpdater(DBUpdater): + def __init__(self, version, ccnet_db_info, seafile_db_info, seahub_db_info): + DBUpdater.__init__(self, version, 'mysql') + self.ccnet_db_info = ccnet_db_info + self.seafile_db_info = seafile_db_info + self.seahub_db_info = seahub_db_info + + def update_ccnet_sql(self, ccnet_sql): + self.apply_sqls(self.ccnet_db_info, ccnet_sql) + + def update_seafile_sql(self, seafile_sql): + self.apply_sqls(self.seafile_db_info, seafile_sql) + + def update_seahub_sql(self, seahub_sql): + self.apply_sqls(self.seahub_db_info, seahub_sql) + + def update_seafevents_sql(self, seafevents_sql): + if self.is_pro: + Utils.info('updating seafevents database...') + self.apply_sqls(self.seahub_db_info, seafevents_sql) + + def get_conn(self, info): + kw = dict( + user=info.username, + passwd=info.password, + db=info.db, + ) + if info.unix_socket: + kw['unix_socket'] = info.unix_socket + else: + kw['host'] = info.host + kw['port'] = info.port + try: + conn = pymysql.connect(**kw) + except Exception as e: + if isinstance(e, pymysql.OperationalError): + msg = str(e.args[1]) + else: + msg = str(e) + Utils.error('Failed to connect to mysql database %s: %s' % (info.db, msg)) + + return conn + + def execute_sql(self, conn, sql): + cursor = conn.cursor() + try: + cursor.execute(sql) + conn.commit() + except Exception as e: + msg = str(e) + Utils.warning('Failed to execute sql: %s' % msg) + + def apply_sqls(self, info, sql_path): + with open(sql_path, 'r') as fp: + lines = fp.read().split(';') + + conn = self.get_conn(info) + + for line in lines: + line = line.strip() + if not line: + continue + else: + self.execute_sql(conn, line) + + +def main(): + skipdb = os.environ.get('SEAFILE_SKIP_DB_UPGRADE', '').lower() + if skipdb in ('1', 'true', 'on'): + print('Database upgrade skipped because SEAFILE_SKIP_DB_UPGRADE=%s' % skipdb) + sys.exit() + version = sys.argv[1] + db_updater = DBUpdater.get_instance(version) + db_updater.update_db() + + return 0 + +if __name__ == '__main__': + main() diff --git a/seafile/docker/seafile-entrypoint.sh b/seafile/docker/seafile-entrypoint.sh new file mode 100755 index 0000000..a65ad42 --- /dev/null +++ b/seafile/docker/seafile-entrypoint.sh @@ -0,0 +1,501 @@ +#!/bin/bash + +# https://raw.githubusercontent.com/Gronis/docker-seafile/master/seafile-entrypoint.sh + +PYTHONPATH="/opt/haiwen/seafile-server-${VERSION}/seafile/lib/python3/site-packages" +DATADIR=${DATADIR:-"/seafile"} +BASEPATH=${BASEPATH:-"/opt/haiwen"} +INSTALLPATH=${INSTALLPATH:-"${BASEPATH}/$(ls -1 ${BASEPATH} | grep -E '^seafile-server-[0-9.-]+')"} +VERSION=$(echo $INSTALLPATH | grep -oE [0-9.]+) +OLD_VERSION=$(cat $DATADIR/version 2> /dev/null || echo $VERSION) +MAJOR_VERSION=$(echo $VERSION | cut -d. -f 1-2) +OLD_MAJOR_VERSION=$(echo $OLD_VERSION | cut -d. -f 1-2) +VIRTUAL_PORT=${VIRTUAL_PORT:-"8000"} + +python3 --version +ldd --version +ls ${BASEPATH}/ccnet + +# Enable debugging mode +# set -x + +set -e +set -u +set -o pipefail + +declare -a errexit_stack +push_exit_on_error_flag() { + errexit_stack+=("$-") +} + +pop_exit_on_error_flag() { + if [ ${#errexit_stack[@]} -gt 0 ]; then + errexit_setting="${errexit_stack[-1]}" + unset 'errexit_stack[-1]' + + if [[ "$errexit_setting" == *"e"* ]]; then + set -e + else + set +e + fi + fi +} + +trapped() { + control_seahub "stop" + control_seafile "stop" +} + +wait_for_database() { + set +u + if [ -n "${MYSQL_SERVER}" ] + then + set -u + echo "Waiting for MYSQL database." + DOCKERIZE_TIMEOUT=${DOCKERIZE_TIMEOUT:-"60s"} + dockerize -timeout ${DOCKERIZE_TIMEOUT} -wait tcp://${MYSQL_SERVER}:${MYSQL_PORT:-3306} + fi +} + +wait_for_seafile() { + echo "Waiting for seafile server to start." + DOCKERIZE_TIMEOUT=${DOCKERIZE_TIMEOUT:-"60s"} + dockerize -timeout ${DOCKERIZE_TIMEOUT} -wait tcp://localhost:8082 +} + +autorun() { + wait_for_database + # Update if neccessary + if [ $OLD_VERSION != $VERSION ]; then + full_update + fi + echo $VERSION > $DATADIR/version + + update_gunicorn_config + update_seahub_config + + # Must disable exit on error + set +e + control_seafile "start" + local RET=$? + set -e + # Try an initial setup on error + if [ ${RET} -eq 255 ] + then + choose_setup + control_seafile "start" + wait_for_seafile + control_seahub "start" + elif [ ${RET} -gt 0 ] + then + exit 1 + fi + + wait_for_seafile + + + if [ ${SEAFILE_FASTCGI:-} ] + then + control_seahub "start-fastcgi" + else + control_seahub "start" + fi + keep_in_foreground +} + +run_only() { + local SH_DB_DIR="${DATADIR}/${SEAHUB_DB_DIR}" + wait_for_database + control_seafile "start" + wait_for_seafile + control_seahub "start" + keep_in_foreground +} + +choose_setup() { + set +u + # If $MYSQL_SERVER is set, we assume MYSQL setup is intended, + # otherwise sqlite + if [ -n "${MYSQL_SERVER}" ] + then + set -u + setup_mysql + else + set -u + setup_sqlite + fi + +} + +setup_mysql() { + echo "setup_mysql" + + wait_for_database + + set +u + OPTIONAL_PARMS="$([ -n "${MYSQL_ROOT_PASSWORD}" ] && printf '%s' "-r ${MYSQL_ROOT_PASSWORD}")" + set -u + + push_exit_on_error_flag + set +e + su - seafile -c ". /tmp/seafile.env; ${INSTALLPATH}/setup-seafile-mysql.sh auto \ + -n "${SEAFILE_NAME}" \ + -i "${SEAFILE_ADDRESS}" \ + -p "${SEAFILE_PORT}" \ + -d "${SEAFILE_DATA_DIR}" \ + -o "${MYSQL_SERVER}" \ + -t "${MYSQL_PORT:-3306}" \ + -u "${MYSQL_USER}" \ + -w "${MYSQL_USER_PASSWORD}" \ + -q "${MYSQL_USER_HOST:-"%"}" \ + ${OPTIONAL_PARMS}" + + setup_seahub + pop_exit_on_error_flag + move_and_link +} + +setup_sqlite() { + echo "setup_sqlite" + # Setup Seafile + su - seafile -c ". /tmp/seafile.env; ${INSTALLPATH}/setup-seafile.sh auto \ + -n "${SEAFILE_NAME}" \ + -i "${SEAFILE_ADDRESS}" \ + -p "${SEAFILE_PORT}" \ + -d "${SEAFILE_DATA_DIR}"" + + setup_seahub + move_and_link +} + +setup_seahub() { + # Setup Seahub + + # From https://github.com/haiwen/seafile-server-installer-cn/blob/master/seafile-server-ubuntu-14-04-amd64-http + sed -i 's/= ask_admin_email()/= '"\"${SEAFILE_ADMIN}\""'/' ${INSTALLPATH}/check_init_admin.py + sed -i 's/= ask_admin_password()/= '"\"${SEAFILE_ADMIN_PW}\""'/' ${INSTALLPATH}/check_init_admin.py + + control_seafile "start" + + su - seafile -c ". /tmp/seafile.env; python3 -t ${INSTALLPATH}/check_init_admin.py" + # su - seafile -c ". /tmp/seafile.env; python3 -m trace -t ${INSTALLPATH}/check_init_admin.py | tee -a /seafile/check_init_admin.log" +} + +move_and_link() { + # As seahub.db is normally in the root dir of seafile (/opt/haiwen) + # SEAHUB_DB_DIR needs to be defined if it should be moved elsewhere under /seafile + local SH_DB_DIR="${DATADIR}/${SEAHUB_DB_DIR}" + # Stop Seafile/hub instances if running + control_seahub "stop" + control_seafile "stop" + + move_files "${SH_DB_DIR}" + link_files "${SH_DB_DIR}" + + if ! su - seafile -c "test -w ${DATADIR}"; then + echo "Updating file permissions" + chown -R seafile:seafile ${DATADIR}/ + fi +} + +update_config() { + VARIABLE_NAME=$1 + CONFIG_FILE=$2 + if [ ! -f "$CONFIG_FILE" ]; then + echo "$CONFIG_FILE does not exists, skipping..." + return + fi + if [ $# -ge 3 ]; then + VARIABLE_VALUE=$3 + else + # Grab variable value from environment if not passed as argument. + set +e + env | grep $VARIABLE_NAME > /dev/null + EXISTS_IN_ENV=$? + set -e + if [[ $EXISTS_IN_ENV -ne 0 ]]; then + # Not passed as argument and not in env, no configuration needed. + return + fi + VARIABLE_VALUE=$(env | grep $VARIABLE_NAME | sed "s/${VARIABLE_NAME}=//g") + fi + if [ $# -ge 4 ] && [[ "$4" != "string" ]]; then + PREPOSTFIX="" + else + PREPOSTFIX="\"" + fi + echo "Updating $VARIABLE_NAME in $CONFIG_FILE." + ESCAPED_VALUE=$(printf '%s\n' "$VARIABLE_VALUE" | sed -e 's/[\/&]/\\&/g') + set +e + grep $VARIABLE_NAME $CONFIG_FILE > /dev/null + EXISTS=$? + set -e + if [[ "${EXISTS}" -eq 0 ]]; then + OLD="$VARIABLE_NAME = .*$" + NEW="$VARIABLE_NAME = ${PREPOSTFIX}${ESCAPED_VALUE}${PREPOSTFIX}" + sed -i "s/${OLD}/${NEW}/g" $CONFIG_FILE + else + NEW="$VARIABLE_NAME = ${PREPOSTFIX}${VARIABLE_VALUE}${PREPOSTFIX}" + su - seafile -c "echo ${NEW} >> $CONFIG_FILE" + fi +} + + +update_gunicorn_config() { + # Must bind 0.0.0.0 instead of 127.0.0.1 + CONFIG_FILE=/seafile/conf/gunicorn.conf.py + update_config "bind" $CONFIG_FILE "0.0.0.0:${VIRTUAL_PORT}" + update_config "daemon" $CONFIG_FILE True boolean +} + +update_seahub_config(){ + CONFIG_FILE=/seafile/conf/seahub_settings.py + update_config "FILE_SERVER_ROOT" $CONFIG_FILE + update_config "SERVICE_URL" $CONFIG_FILE +} + +update_gunicorn_config_disable_daemon_mode() { + CONFIG_FILE=/seafile/conf/gunicorn.conf.py + update_config "daemon" $CONFIG_FILE False boolean +} + +move_files() { + MOVE_LIST=( + "${BASEPATH}/ccnet:${DATADIR}/ccnet" + "${BASEPATH}/conf:${DATADIR}/conf" + "${BASEPATH}/seafile-data:${DATADIR}/seafile-data" + "${BASEPATH}/seahub-data:${DATADIR}/seahub-data" + "${INSTALLPATH}/seahub/media/avatars:${DATADIR}/seahub-data/avatars" + ) + for SEADIR in ${MOVE_LIST[@]} + do + ARGS=($(echo $SEADIR | tr ":" "\n")) + if [ -e "${ARGS[0]}" ] && [ ! -e "${ARGS[1]}" ] + then + echo "Copying ${ARGS[0]} => ${ARGS[1]}" + local PARENT=$(dirname ${ARGS[1]}) + if [ ! -e $PARENT ] + then + mkdir -p $PARENT + fi + cp -a ${ARGS[0]}/ ${ARGS[1]} + fi + if [ -e "${ARGS[0]}" ] && [ ! -L "${ARGS[0]}" ] + then + echo "Dropping ${ARGS[0]}" + rm -rf ${ARGS[0]} + fi + done + + if [ -e "${BASEPATH}/seahub.db" -a ! -L "${BASEPATH}/seahub.db" ] + then + mv ${BASEPATH}/seahub.db ${1}/ + fi +} + +link_files() { + LINK_LIST=( + "${DATADIR}/ccnet:${BASEPATH}/ccnet" + "${DATADIR}/conf:${BASEPATH}/conf" + "${DATADIR}/seafile-data:${BASEPATH}/seafile-data" + "${DATADIR}/seahub-data:${BASEPATH}/seahub-data" + "${DATADIR}/seahub-data/avatars:${INSTALLPATH}/seahub/media/avatars" + ) + for SEADIR in ${LINK_LIST[@]} + do + ARGS=($(echo $SEADIR | tr ":" "\n")) + if [ ! -L "${ARGS[1]}" ] && [ -e "${ARGS[0]}" ] + then + echo "Linking ${ARGS[1]} => ${ARGS[0]}" + su - seafile -c "ln -sf ${ARGS[0]} ${ARGS[1]}" + fi + done + if [ -e "${SH_DB_DIR}/seahub.db" -a ! -L "${BASEPATH}/seahub.db" ] + then + ln -s ${1}/seahub.db ${BASEPATH}/seahub.db + fi + +} + +keep_in_foreground() { + echo "Seafile startup completed" + echo "Container will stop if any seafile related process exits" + echo "" + # As there seems to be no way to let Seafile processes run in the foreground we + # need a foreground process. This has a dual use as a supervisor script because + # as soon as one process is not running, the command returns an exit code >0 + # leading to a script abortion thanks to "set -e". + while true + do + for SEAFILE_PROC in "seafile-control" "seaf-server" "gunicorn" + do + pkill -0 -f "${SEAFILE_PROC}" + sleep 1 + done + sleep 20 + done +} + +prepare_env() { + cat << _EOF_ > /tmp/seafile.env + export LANG='en_US.UTF-8' + export LC_ALL='en_US.UTF-8' + export CCNET_CONF_DIR="${BASEPATH}/ccnet" + export SEAFILE_CONF_DIR="${SEAFILE_DATA_DIR}" + export SEAFILE_CENTRAL_CONF_DIR="${BASEPATH}/conf" + export PYTHONPATH=${INSTALLPATH}/seafile/lib/python3.6/site-packages:${INSTALLPATH}/seafile/lib64/python3.6/site-packages:${INSTALLPATH}/seahub:${INSTALLPATH}/seahub/thirdpart:${INSTALLPATH}/seafile/lib/python3.6/site-packages:${INSTALLPATH}/seafile/lib64/python3.6/site-packages:${PYTHONPATH:-} + +_EOF_ +} + +control_seafile() { + push_exit_on_error_flag + set +e + su - seafile -c ". /tmp/seafile.env; ${INSTALLPATH}/seafile.sh "$@"" + local RET=$? + if [ $RET -gt 0 ]; then + print_log + fi + pop_exit_on_error_flag + return ${RET} +} + +control_seahub() { + push_exit_on_error_flag + set +e + su - seafile -c ". /tmp/seafile.env; ${INSTALLPATH}/seahub.sh "$@"" + local RET=$? + + if [ $RET -gt 0 ] && [[ "$@" == "start" ]]; then + update_gunicorn_config_disable_daemon_mode + su - seafile -c ". /tmp/seafile.env; ${INSTALLPATH}/seahub.sh "$@"" + RET=$? + fi + + if [ $RET -gt 0 ]; then + print_log + fi + pop_exit_on_error_flag + return ${RET} +} + +print_log() { + if [ -e $INSTALLPATH/../logs ]; then + return + fi + sleep 2 + for file in $INSTALLPATH/../logs/*; do + if [ -f "$file" ]; then + echo "-----------------------------" + echo "$(basename "$file"):" + echo "-----------------------------" + cat "$file" + echo "" + fi + done +} + +full_update(){ + EXECUTE="" + echo "" + echo "---------------------------------------" + echo "Upgrading from $OLD_VERSION to $VERSION" + echo "---------------------------------------" + echo "" + wait_for_database + # Iterate through all the major upgrade scripts and apply them + for i in `ls ${INSTALLPATH}/upgrade/`; do + if [ `echo $i | grep "upgrade_${OLD_MAJOR_VERSION}"` ]; then + EXECUTE=1 + fi + if [ $EXECUTE ] && [ `echo $i | grep upgrade` ]; then + echo "Running update $i" + update $i || exit + fi + done + # When all the major upgrades are done, perform a minor upgrade + if [ -z $EXECUTE ]; then + update minor-upgrade.sh + echo $VERSION > $DATADIR/version + fi +} + +update(){ + su - seafile -c ". /tmp/seafile.env; echo -ne '\n' | ${INSTALLPATH}/upgrade/$@" + local RET=$? + sleep 1 + return ${RET} +} + +collect_garbage(){ + su - seafile -c ". /tmp/seafile.env; ${INSTALLPATH}/seaf-gc.sh $@" + local RET=$? + sleep 1 + return ${RET} +} + +diagnose(){ + su - seafile -c ". /tmp/seafile.env; ${INSTALLPATH}/seaf-fsck.sh $@" + local RET=$? + sleep 1 + return ${RET} +} + +maintenance(){ + local SH_DB_DIR="${DATADIR}/${SEAHUB_DB_DIR}" + # Linking must always be done + link_files "${SH_DB_DIR}" + echo "" + echo "---------------------------------------" + echo "Running in maintenance mode" + echo "---------------------------------------" + echo "" + tail -f /dev/null +} + + +# Fill vars with defaults if empty +if [ -z ${MODE+x} ]; then + MODE=${1:-"run"} +fi + +SEAFILE_DATA_DIR=${SEAFILE_DATA_DIR:-"${DATADIR}/seafile-data"} +SEAFILE_PORT=${SEAFILE_PORT:-8082} +SEAHUB_DB_DIR=${SEAHUB_DB_DIR:-} + +prepare_env + +trap trapped SIGINT SIGTERM +move_and_link +case $MODE in + "autorun" | "run") + autorun + ;; + "setup" | "setup_mysql") + setup_mysql + ;; + "setup_sqlite") + setup_sqlite + ;; + "setup_seahub") + setup_seahub + ;; + "setup_only") + choose_setup + ;; + "run_only") + run_only + ;; + "update") + full_update + ;; + "diagnose") + diagnose + ;; + "collect_garbage") + collect_garbage + ;; + "maintenance") + maintenance + ;; +esac