seafile-containerized/scripts_7.1/bootstrap.py
2020-04-13 16:02:38 +08:00

230 lines
8 KiB
Python
Executable file

#!/usr/bin/env python3
#coding: UTF-8
"""
Bootstraping seafile server, letsencrypt (verification & cron job).
"""
import argparse
import os
from os.path import abspath, basename, exists, dirname, join, isdir
import shutil
import sys
import uuid
import time
from utils import (
call, get_conf, get_install_dir, loginfo,
get_script, render_template, get_seafile_version, eprint,
cert_has_valid_days, get_version_stamp_file, update_version_stamp,
wait_for_mysql, wait_for_nginx, read_version_stamp
)
seafile_version = get_seafile_version()
installdir = get_install_dir()
topdir = dirname(installdir)
shared_seafiledir = '/shared/seafile'
ssl_dir = '/shared/seafile/ssl'
generated_dir = '/bootstrap/generated'
def init_letsencrypt():
loginfo('Preparing for letsencrypt ...')
wait_for_nginx()
if not exists(ssl_dir):
os.mkdir(ssl_dir)
domain = get_conf('SEAFILE_SERVER_HOSTNAME', 'seafile.example.com')
context = {
'ssl_dir': ssl_dir,
'domain': domain,
}
render_template(
'/templates/letsencrypt.cron.template',
join(generated_dir, 'letsencrypt.cron'),
context
)
ssl_crt = '/shared/seafile/ssl/{}.crt'.format(domain)
if exists(ssl_crt):
loginfo('Found existing cert file {}'.format(ssl_crt))
if cert_has_valid_days(ssl_crt, 30):
loginfo('Skip letsencrypt verification since we have a valid certificate')
if exists(join(ssl_dir, 'letsencrypt')):
# Create a crontab to auto renew the cert for letsencrypt.
call('/scripts/auto_renew_crt.sh {0} {1}'.format(ssl_dir, domain))
return
loginfo('Starting letsencrypt verification')
# Create a temporary nginx conf to start a server, which would accessed by letsencrypt
context = {
'https': False,
'domain': domain,
}
render_template('/templates/seafile.nginx.conf.template',
'/etc/nginx/sites-enabled/default', context)
call('nginx -s reload')
time.sleep(2)
call('/scripts/ssl.sh {0} {1}'.format(ssl_dir, domain))
# if call('/scripts/ssl.sh {0} {1}'.format(ssl_dir, domain), check_call=False) != 0:
# eprint('Now waiting 1000s for postmortem')
# time.sleep(1000)
# sys.exit(1)
call('/scripts/auto_renew_crt.sh {0} {1}'.format(ssl_dir, domain))
# Create a crontab to auto renew the cert for letsencrypt.
def generate_local_nginx_conf():
# Now create the final nginx configuratin
domain = get_conf('SEAFILE_SERVER_HOSTNAME', 'seafile.example.com')
context = {
'https': is_https(),
'domain': domain,
}
render_template(
'/templates/seafile.nginx.conf.template',
'/etc/nginx/sites-enabled/default',
context
)
def is_https():
return get_conf('SEAFILE_SERVER_LETSENCRYPT', 'false').lower() == 'true'
def parse_args():
ap = argparse.ArgumentParser()
ap.add_argument('--parse-ports', action='store_true')
return ap.parse_args()
def generate_seafevents_conf():
user = get_conf('MYSQL_USER', 'root')
passwd = get_conf('DB_ROOT_PASSWD', '')
if user != 'root':
passwd = get_conf('DB_USER_PASSWD', '123')
host = get_conf('DB_HOST', '127.0.0.1')
context = """
[DATABASE]
type=mysql
username=%s
password=%s
name=seahub_db
host=%s
[FILE HISTORY]
enabled = true
suffix=txt,pdf,md,doc,docs
""" % (user, passwd, host)
with open(join(topdir, 'conf', 'seafevents.conf'), 'a+') as fp:
fp.write('\n')
fp.write(context)
fp.write('\n')
def init_seafile_server():
version_stamp_file = get_version_stamp_file()
if exists(join(shared_seafiledir, 'seafile-data')):
if not exists(version_stamp_file):
update_version_stamp(os.environ['SEAFILE_VERSION'])
# sysbol link unlink after docker finish.
latest_version_dir='/opt/seafile/seafile-server-latest'
current_version_dir='/opt/seafile/' + get_conf('SEAFILE_SERVER', 'seafile-server') + '-' + read_version_stamp()
if not exists(latest_version_dir):
call('ln -sf ' + current_version_dir + ' ' + latest_version_dir)
loginfo('Skip running setup-seafile-mysql.py because there is existing seafile-data folder.')
log_dir = join(shared_seafiledir, 'logs')
if not exists(log_dir):
os.mkdir(log_dir)
files_to_link = ['conf', 'ccnet', 'seafile-data', 'seahub-data', 'pro-data', 'logs']
for fn in files_to_link:
src = join(shared_seafiledir, fn)
dst = join(topdir, fn)
if not exists(dst) and exists(src):
call('ln -sf ' + src + ' ' + dst)
return
loginfo('Now running setup-seafile-mysql.py in auto mode.')
env = {
'SERVER_NAME': 'seafile',
'SERVER_IP': get_conf('SEAFILE_SERVER_HOSTNAME', '127.0.0.1'),
'MYSQL_USER': get_conf('MYSQL_USER', 'root'),
'MYSQL_USER_PASSWD': get_conf('DB_USER_PASSWD', '123'),
'MYSQL_USER_HOST': get_conf('DB_HOST', '127.0.0.1'),
'MYSQL_HOST': get_conf('DB_HOST', '127.0.0.1'),
'MYSQL_ROOT_PASSWD': get_conf('DB_ROOT_PASSWD', ''),
'USE_EXISTING_DB': get_conf('USE_EXISTING_DB', '0'),
'CCNET_DB': get_conf('CCNET_DB', 'ccnet_db'),
'SEAFILE_DB': get_conf('SEAFILE_DB', 'seafile_db'),
'SEAHUB_DB': get_conf('SEAHUB_DB', 'seahub_db')
}
# Change the script to allow mysql root password to be empty
call('''sed -i -e 's/if not mysql_root_passwd/if not mysql_root_passwd and "MYSQL_ROOT_PASSWD" not in os.environ/g' {}'''
.format(get_script('setup-seafile-mysql.py')))
call('''sed -i -e '/def validate_mysql_user_host(self, host)/a \ \ \ \ \ \ \ \ return host' {}'''
.format(get_script('setup-seafile-mysql.py')))
call('''sed -i -e '/def validate_mysql_host(self, host)/a \ \ \ \ \ \ \ \ return host' {}'''
.format(get_script('setup-seafile-mysql.py')))
setup_script = get_script('setup-seafile-mysql.sh')
call('{} auto -n seafile'.format(setup_script), env=env)
domain = get_conf('SEAFILE_SERVER_HOSTNAME', 'seafile.example.com')
proto = 'https' if is_https() else 'http'
with open(join(topdir, 'conf', 'seahub_settings.py'), 'a+') as fp:
fp.write('\n')
fp.write(
"""
CACHES = {
'default': {
'BACKEND': 'django_pylibmc.memcached.PyLibMCCache',
'LOCATION': '%s:11211',
},
'locmem': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
},
}
COMPRESS_CACHE_BACKEND = 'locmem'
FILE_SERVER_ROOT = '%s://%s/seafhttp'
""" % (get_conf('MEMCACHED', 'memcached'), proto, domain))
fp.write('\n')
fp.write("TIME_ZONE = '{time_zone}'".format(time_zone=os.getenv('TIME_ZONE', default='Etc/UTC')))
fp.write('\n')
# By default ccnet-server binds to the unix socket file
# "/opt/seafile/ccnet/ccnet.sock", but /opt/seafile/ccnet/ is a mounted
# volume from the docker host, and on windows and some linux environment
# it's not possible to create unix sockets in an external-mounted
# directories. So we change the unix socket file path to
# "/opt/seafile/ccnet.sock" to avoid this problem.
with open(join(topdir, 'conf', 'ccnet.conf'), 'a+') as fp:
fp.write('\n')
fp.write('[Client]\n')
fp.write('UNIX_SOCKET = /opt/seafile/ccnet.sock\n')
fp.write('\n')
# After the setup script creates all the files inside the
# container, we need to move them to the shared volume
#
# e.g move "/opt/seafile/seafile-data" to "/shared/seafile/seafile-data"
log_dir = join(topdir, 'logs')
if not exists(log_dir):
os.mkdir(log_dir)
files_to_copy = ['conf', 'ccnet', 'seafile-data', 'seahub-data', 'pro-data', 'logs']
for fn in files_to_copy:
src = join(topdir, fn)
dst = join(shared_seafiledir, fn)
if not exists(dst) and exists(src):
shutil.move(src, shared_seafiledir)
call('ln -sf ' + join(shared_seafiledir, fn) + ' ' + src)
generate_seafevents_conf()
loginfo('Updating version stamp')
update_version_stamp(os.environ['SEAFILE_VERSION'])