copy scripts into Dockerfile folder

This commit is contained in:
Gerrit Gogel 2021-01-18 00:45:50 +01:00
parent 32233add11
commit bcd36ebbe3
8 changed files with 985 additions and 0 deletions

View file

@ -0,0 +1,37 @@
#!/bin/bash
set -e
ssldir=${1:?"error params"}
domain=${2:?"error params"}
letsencryptdir=$ssldir/letsencrypt
letsencrypt_script=$letsencryptdir/acme_tiny.py
ssl_account_key=${domain}.account.key
ssl_csr=${domain}.csr
ssl_key=${domain}.key
ssl_crt=${domain}.crt
renew_cert_script=/scripts/renew_cert.sh
if [[ ! -x ${renew_cert_script} ]]; then
cat > ${renew_cert_script} << EOF
#!/bin/bash
python3 ${letsencrypt_script} --account-key ${ssldir}/${ssl_account_key} --csr ${ssldir}/${ssl_csr} --acme-dir /var/www/challenges/ > ${ssldir}/${ssl_crt} || exit
$(which nginx) -s reload
EOF
chmod u+x ${renew_cert_script}
if [[ ! -d "/var/www/challenges" ]]; then
mkdir -p /var/www/challenges
fi
cat >> /etc/crontab << EOF
00 1 1 * * root /scripts/renew_cert.sh 2>> /var/log/acme_tiny.log
EOF
echo 'Created a crontab to auto renew the cert for letsencrypt.'
else
echo 'Found existing the script for renew the cert.'
echo 'Skip create the crontab for letscncrypt since maybe we have created before.'
fi

View file

@ -0,0 +1,234 @@
#!/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/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/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,
}
if not os.path.isfile('/shared/nginx/conf/seafile.nginx.conf'):
render_template('/templates/seafile.nginx.conf.template',
'/etc/nginx/sites-enabled/seafile.nginx.conf', 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,
}
if not os.path.isfile('/shared/nginx/conf/seafile.nginx.conf'):
render_template(
'/templates/seafile.nginx.conf.template',
'/etc/nginx/sites-enabled/seafile.nginx.conf',
context
)
nginx_etc_file = '/etc/nginx/sites-enabled/seafile.nginx.conf'
nginx_shared_file = '/shared/nginx/conf/seafile.nginx.conf'
call('mv {0} {1} && ln -sf {1} {0}'.format(nginx_etc_file, nginx_shared_file))
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 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.')
return
loginfo('Now running setup-seafile-mysql.py in auto mode.')
env = {
'SERVER_NAME': 'seafile',
'SERVER_IP': get_conf('SEAFILE_SERVER_HOSTNAME', 'seafile.example.com'),
'MYSQL_USER': 'seafile',
'MYSQL_USER_PASSWD': str(uuid.uuid4()),
'MYSQL_USER_HOST': '%.%.%.%',
'MYSQL_HOST': get_conf('DB_HOST','127.0.0.1'),
# Default MariaDB root user has empty password and can only connect from localhost.
'MYSQL_ROOT_PASSWD': get_conf('DB_ROOT_PASSWD', ''),
}
# 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')))
# Change the script to disable check MYSQL_USER_HOST
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': 'memcached:11211',
},
'locmem': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
},
}
COMPRESS_CACHE_BACKEND = 'locmem'""")
fp.write('\n')
fp.write("TIME_ZONE = '{time_zone}'".format(time_zone=os.getenv('TIME_ZONE',default='Etc/UTC')))
fp.write('\n')
fp.write('FILE_SERVER_ROOT = "{proto}://{domain}/seafhttp"'.format(proto=proto, domain=domain))
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')
# Disabled the Elasticsearch process on Seafile-container
# Connection to the Elasticsearch-container
if os.path.exists(join(topdir, 'conf', 'seafevents.conf')):
with open(join(topdir, 'conf', 'seafevents.conf'), 'r') as fp:
fp_lines = fp.readlines()
if '[INDEX FILES]\n' in fp_lines:
insert_index = fp_lines.index('[INDEX FILES]\n') + 1
insert_lines = ['es_port = 9200\n', 'es_host = elasticsearch\n', 'external_es_server = true\n']
for line in insert_lines:
fp_lines.insert(insert_index, line)
# office
if '[OFFICE CONVERTER]\n' in fp_lines:
insert_index = fp_lines.index('[OFFICE CONVERTER]\n') + 1
insert_lines = ['host = 127.0.0.1\n', 'port = 6000\n']
for line in insert_lines:
fp_lines.insert(insert_index, line)
with open(join(topdir, 'conf', 'seafevents.conf'), 'w') as fp:
fp.writelines(fp_lines)
# office
with open(join(topdir, 'conf', 'seahub_settings.py'), 'r') as fp:
fp_lines = fp.readlines()
if "OFFICE_CONVERTOR_ROOT = 'http://127.0.0.1:6000/'\n" not in fp_lines:
fp_lines.append("OFFICE_CONVERTOR_ROOT = 'http://127.0.0.1:6000/'\n")
with open(join(topdir, 'conf', 'seahub_settings.py'), 'w') as fp:
fp.writelines(fp_lines)
# Modify seafdav config
if os.path.exists(join(topdir, 'conf', 'seafdav.conf')):
with open(join(topdir, 'conf', 'seafdav.conf'), 'r') as fp:
fp_lines = fp.readlines()
if 'share_name = /\n' in fp_lines:
replace_index = fp_lines.index('share_name = /\n')
replace_line = 'share_name = /seafdav\n'
fp_lines[replace_index] = replace_line
with open(join(topdir, 'conf', 'seafdav.conf'), 'w') as fp:
fp.writelines(fp_lines)
# 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"
files_to_copy = ['conf', 'ccnet', 'seafile-data', 'seahub-data', 'pro-data']
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)
loginfo('Updating version stamp')
update_version_stamp(os.environ['SEAFILE_VERSION'])

View file

@ -0,0 +1,54 @@
#!/bin/bash
set -e
set -o pipefail
if [[ $SEAFILE_BOOTSRAP != "" ]]; then
exit 0
fi
if [[ $TIME_ZONE != "" ]]; then
time_zone=/usr/share/zoneinfo/$TIME_ZONE
if [[ ! -e $time_zone ]]; then
echo "invalid time zone"
exit 1
else
ln -snf $time_zone /etc/localtime
echo "$TIME_ZONE" > /etc/timezone
fi
fi
dirs=(
conf
ccnet
seafile-data
seahub-data
pro-data
seafile-license.txt
)
for d in ${dirs[*]}; do
src=/shared/seafile/$d
if [[ -e $src ]]; then
rm -rf /opt/seafile/$d && ln -sf $src /opt/seafile
fi
done
if [[ -e /shared/logs/seafile ]]; then
mv /shared/logs/seafile /shared/seafile/logs
rm -rf /opt/seafile/logs && ln -sf /shared/seafile/logs /opt/seafile/
else
mkdir -p /shared/seafile/logs && ln -sf /shared/seafile/logs /opt/seafile/
fi
if [[ ! -e /shared/logs/var-log ]]; then
mkdir -p /shared/logs/ && mv /var/log /shared/logs/var-log
fi
rm -rf /var/log && ln -sf /shared/logs/var-log /var/log
mkdir -p /shared/nginx/conf/
if [[ -e /shared/nginx/conf/seafile.nginx.conf ]]; then
rm -rf /etc/nginx/sites-enabled/seafile.nginx.conf && \
ln -sf /shared/nginx/conf/seafile.nginx.conf /etc/nginx/sites-enabled
fi

View file

@ -0,0 +1,37 @@
#!/bin/bash
set -e
# Before
SEAFILE_DIR=/opt/seafile/seafile-server-latest
if [[ $SEAFILE_SERVER != *"pro"* ]]; then
echo "Seafile CE: Stop Seafile to perform offline garbage collection."
$SEAFILE_DIR/seafile.sh stop
echo "Waiting for the server to shut down properly..."
sleep 5
else
echo "Seafile Pro: Perform online garbage collection."
fi
# Do it
(
set +e
$SEAFILE_DIR/seaf-gc.sh "$@" | tee -a /var/log/gc.log
# We want to presevent the exit code of seaf-gc.sh
exit "${PIPESTATUS[0]}"
)
gc_exit_code=$?
# After
if [[ $SEAFILE_SERVER != *"pro"* ]]; then
echo "Giving the server some time..."
sleep 3
$SEAFILE_DIR/seafile.sh start
fi
exit $gc_exit_code

View file

@ -0,0 +1,46 @@
#!/bin/bash
set -e
ssldir=${1:?"error params"}
domain=${2:?"error params"}
letsencryptdir=$ssldir/letsencrypt
letsencrypt_script=$letsencryptdir/acme_tiny.py
ssl_account_key=${domain}.account.key
ssl_csr=${domain}.csr
ssl_key=${domain}.key
ssl_crt=${domain}.crt
mkdir -p /var/www/challenges && chmod -R 777 /var/www/challenges
mkdir -p $ssldir
if ! [[ -d $letsencryptdir ]]; then
git clone git://github.com/diafygi/acme-tiny.git $letsencryptdir
else
cd $letsencryptdir
git pull origin master:master
fi
cd $ssldir
if [[ ! -e ${ssl_account_key} ]]; then
openssl genrsa 4096 > ${ssl_account_key}
fi
if [[ ! -e ${ssl_key} ]]; then
openssl genrsa 4096 > ${ssl_key}
fi
if [[ ! -e ${ssl_csr} ]]; then
openssl req -new -sha256 -key ${ssl_key} -subj "/CN=$domain" > $ssl_csr
fi
python3 $letsencrypt_script --account-key ${ssl_account_key} --csr $ssl_csr --acme-dir /var/www/challenges/ > ./signed.crt
curl -sSL -o intermediate.pem https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
cat signed.crt intermediate.pem > ${ssl_crt}
nginx -s reload
echo "Nginx reloaded."

View file

@ -0,0 +1,86 @@
#!/usr/bin/env python3
#coding: UTF-8
"""
Starts the seafile/seahub server and watches the controller process. It is
the entrypoint command of the docker container.
"""
import json
import os
from os.path import abspath, basename, exists, dirname, join, isdir
import shutil
import sys
import time
from utils import (
call, get_conf, get_install_dir, get_script, get_command_output,
render_template, wait_for_mysql, setup_logging
)
from upgrade import check_upgrade
from bootstrap import init_seafile_server, is_https, init_letsencrypt, generate_local_nginx_conf
shared_seafiledir = '/shared/seafile'
ssl_dir = '/shared/ssl'
generated_dir = '/bootstrap/generated'
installdir = get_install_dir()
topdir = dirname(installdir)
def watch_controller():
maxretry = 4
retry = 0
while retry < maxretry:
controller_pid = get_command_output('ps aux | grep seafile-controller | grep -v grep || true').strip()
garbage_collector_pid = get_command_output('ps aux | grep /scripts/gc.sh | grep -v grep || true').strip()
if not controller_pid and not garbage_collector_pid:
retry += 1
else:
retry = 0
time.sleep(5)
print('seafile controller exited unexpectedly.')
sys.exit(1)
def main():
if not exists(shared_seafiledir):
os.mkdir(shared_seafiledir)
if not exists(generated_dir):
os.makedirs(generated_dir)
if is_https():
init_letsencrypt()
generate_local_nginx_conf()
call('nginx -s reload')
wait_for_mysql()
init_seafile_server()
check_upgrade()
os.chdir(installdir)
admin_pw = {
'email': get_conf('SEAFILE_ADMIN_EMAIL', 'me@example.com'),
'password': get_conf('SEAFILE_ADMIN_PASSWORD', 'asecret'),
}
password_file = join(topdir, 'conf', 'admin.txt')
with open(password_file, 'w') as fp:
json.dump(admin_pw, fp)
try:
call('{} start'.format(get_script('seafile.sh')))
call('{} start'.format(get_script('seahub.sh')))
finally:
if exists(password_file):
os.unlink(password_file)
print('seafile server is running now.')
try:
watch_controller()
except KeyboardInterrupt:
print('Stopping seafile server.')
sys.exit(0)
if __name__ == '__main__':
setup_logging()
main()

View file

@ -0,0 +1,193 @@
#!/usr/bin/env python3
#coding: UTF-8
"""
This script is used to run proper upgrade scripts automatically.
"""
import json
import re
import glob
import logging
import os
from os.path import abspath, basename, exists, dirname, join, isdir, islink
import shutil
import sys
import time
import configparser
from utils import (
call, get_install_dir, get_script, get_command_output, replace_file_pattern,
read_version_stamp, wait_for_mysql, update_version_stamp, loginfo
)
installdir = get_install_dir()
topdir = dirname(installdir)
logger = logging.getLogger(__name__)
def collect_upgrade_scripts(from_version, to_version):
"""
Give the current installed version, calculate which upgrade scripts we need
to run to upgrade it to the latest verison.
For example, given current version 5.0.1 and target version 6.1.0, and these
upgrade scripts:
upgrade_4.4_5.0.sh
upgrade_5.0_5.1.sh
upgrade_5.1_6.0.sh
upgrade_6.0_6.1.sh
We need to run upgrade_5.0_5.1.sh, upgrade_5.1_6.0.sh, and upgrade_6.0_6.1.sh.
"""
from_major_ver = '.'.join(from_version.split('.')[:2])
to_major_ver = '.'.join(to_version.split('.')[:2])
scripts = []
for fn in sorted(glob.glob(join(installdir, 'upgrade', 'upgrade_*_*.sh'))):
va, vb = parse_upgrade_script_version(fn)
if va >= from_major_ver and vb <= to_major_ver:
scripts.append(fn)
return scripts
def parse_upgrade_script_version(script):
script = basename(script)
m = re.match(r'upgrade_([0-9+.]+)_([0-9+.]+).sh', basename(script))
return m.groups()
def run_script_and_update_version_stamp(script, new_version):
logging.info('Running script %s', script)
replace_file_pattern(script, 'read dummy', '')
call(script)
update_version_stamp(new_version)
def is_minor_upgrade(v1, v2):
get_major_version = lambda x: x.split('.')[:2]
return v1 != v2 and get_major_version(v1) == get_major_version(v2)
def fix_media_symlinks(current_version):
"""
If the container was recreated and it's not a minor/major upgrade,
we need to fix the media/avatars and media/custom symlink.
"""
media_dir = join(
installdir,
'seafile-server-{}/seahub/media'.format(current_version)
)
avatars_dir = join(media_dir, 'avatars')
if not islink(avatars_dir):
logger.info('The container was recreated, running minor-upgrade.sh to fix the media symlinks')
run_minor_upgrade(current_version)
def run_minor_upgrade(current_version):
minor_upgrade_script = join(installdir, 'upgrade', 'minor-upgrade.sh')
run_script_and_update_version_stamp(minor_upgrade_script, current_version)
def fix_custom_dir():
real_custom_dir = '/shared/seafile/seahub-data/custom'
if not exists(real_custom_dir):
os.mkdir(real_custom_dir)
def fix_ccent_conf():
ccnet_conf_path = '/shared/seafile/conf/ccnet.conf'
if exists(ccnet_conf_path):
cp = configparser.ConfigParser({})
try:
cp.read(ccnet_conf_path)
except configparser.DuplicateSectionError as e:
with open(ccnet_conf_path, 'r+') as fp:
content_list = fp.readlines()
aim = '[Client]\n'
count = content_list.count(aim)
if count > 1:
new_content_list = list()
client_port_index = -1
for index, text in enumerate(content_list):
if text == aim and 'PORT = ' in content_list[index + 1]:
client_port_index = index + 1
continue
if index == client_port_index:
client_port_index = -1
continue
new_content_list.append(text)
new_content = ''.join(new_content_list)
fp.seek(0)
fp.truncate()
fp.write(new_content)
print('\n------------------------------')
print('Fix ccnet conf success')
print('------------------------------\n')
def fix_seafevents_conf():
seafevents_conf_path = '/shared/seafile/conf/seafevents.conf'
seahub_conf_path = '/shared/seafile/conf/seahub_settings.py'
pro_data_dir = '/shared/seafile/pro-data/'
if exists(seafevents_conf_path):
os.makedirs(pro_data_dir, exist_ok=True)
with open(seafevents_conf_path, 'r') as fp:
fp_lines = fp.readlines()
if 'port = 6000\n' in fp_lines:
return
if '[INDEX FILES]\n' in fp_lines and 'external_es_server = true\n' not in fp_lines:
insert_index = fp_lines.index('[INDEX FILES]\n') + 1
insert_lines = ['es_port = 9200\n', 'es_host = elasticsearch\n', 'external_es_server = true\n']
for line in insert_lines:
fp_lines.insert(insert_index, line)
if '[OFFICE CONVERTER]\n' in fp_lines and 'port = 6000\n' not in fp_lines:
insert_index = fp_lines.index('[OFFICE CONVERTER]\n') + 1
insert_lines = ['host = 127.0.0.1\n', 'port = 6000\n']
for line in insert_lines:
fp_lines.insert(insert_index, line)
with open(seafevents_conf_path, 'w') as fp:
fp.writelines(fp_lines)
with open(seahub_conf_path, 'r') as fp:
fp_lines = fp.readlines()
if "OFFICE_CONVERTOR_ROOT = 'http://127.0.0.1:6000/'\n" not in fp_lines:
fp_lines.append("OFFICE_CONVERTOR_ROOT = 'http://127.0.0.1:6000/'\n")
with open(seahub_conf_path, 'w') as fp:
fp.writelines(fp_lines)
print('\n------------------------------')
print('Fix seafevents conf success')
print('------------------------------\n')
def check_upgrade():
fix_custom_dir()
fix_ccent_conf()
fix_seafevents_conf()
last_version = read_version_stamp()
current_version = os.environ['SEAFILE_VERSION']
if last_version == current_version:
fix_media_symlinks(current_version)
return
elif is_minor_upgrade(last_version, current_version):
run_minor_upgrade(current_version)
return
# Now we do the major upgrade
scripts_to_run = collect_upgrade_scripts(from_version=last_version, to_version=current_version)
for script in scripts_to_run:
loginfo('Running scripts {}'.format(script))
# Here we use a trick: use a version stamp like 6.1.0 to prevent running
# all upgrade scripts before 6.1 again (because 6.1 < 6.1.0 in python)
new_version = parse_upgrade_script_version(script)[1] + '.0'
run_script_and_update_version_stamp(script, new_version)
update_version_stamp(current_version)
def main():
wait_for_mysql()
os.chdir(installdir)
check_upgrade()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,298 @@
#!/usr/bin/env python3
# coding: UTF-8
from configparser import ConfigParser
from contextlib import contextmanager
import os
import datetime
from os.path import abspath, basename, exists, dirname, join, isdir, expanduser
import platform
import sys
import subprocess
import time
import logging
import logging.config
import click
import termcolor
import colorlog
import pymysql
logger = logging.getLogger('.utils')
DEBUG_ENABLED = os.environ.get('SEAFILE_DOCKER_VERBOSE', '').lower() in ('true', '1', 'yes')
def eprint(*a, **kw):
kw['file'] = sys.stderr
print(*a, **kw)
def identity(msg, *a, **kw):
return msg
colored = identity if not os.isatty(sys.stdin.fileno()) else termcolor.colored
red = lambda s: colored(s, 'red')
green = lambda s: colored(s, 'green')
def underlined(msg):
return '\x1b[4m{}\x1b[0m'.format(msg)
def sudo(*a, **kw):
call('sudo ' + a[0], *a[1:], **kw)
def _find_flag(args, *opts, **kw):
is_flag = kw.get('is_flag', False)
if is_flag:
return any([opt in args for opt in opts])
else:
for opt in opts:
try:
return args[args.index(opt) + 1]
except ValueError:
pass
def call(*a, **kw):
dry_run = kw.pop('dry_run', False)
quiet = kw.pop('quiet', DEBUG_ENABLED)
cwd = kw.get('cwd', os.getcwd())
check_call = kw.pop('check_call', True)
reduct_args = kw.pop('reduct_args', [])
if not quiet:
toprint = a[0]
args = [x.strip('"') for x in a[0].split() if '=' not in x]
for arg in reduct_args:
value = _find_flag(args, arg)
toprint = toprint.replace(value, '{}**reducted**'.format(value[:3]))
logdbg('calling: ' + green(toprint))
logdbg('cwd: ' + green(cwd))
kw.setdefault('shell', True)
if not dry_run:
if check_call:
return subprocess.check_call(*a, **kw)
else:
return subprocess.Popen(*a, **kw).wait()
@contextmanager
def cd(path):
path = expanduser(path)
olddir = os.getcwd()
os.chdir(path)
try:
yield
finally:
os.chdir(olddir)
def must_makedir(p):
p = expanduser(p)
if not exists(p):
logger.info('created folder %s', p)
os.makedirs(p)
else:
logger.debug('folder %s already exists', p)
def setup_colorlog():
logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
'colored': {
'()': 'colorlog.ColoredFormatter',
'format': "%(log_color)s[%(asctime)s]%(reset)s %(blue)s%(message)s",
'datefmt': '%m/%d/%Y %H:%M:%S',
},
},
'handlers': {
'default': {
'level': 'INFO',
'formatter': 'colored',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'': {
'handlers': ['default'],
'level': 'INFO',
'propagate': True
},
'django.request': {
'handlers': ['default'],
'level': 'WARN',
'propagate': False
},
}
})
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(
logging.WARNING)
def setup_logging(level=logging.INFO):
kw = {
'format': '[%(asctime)s][%(module)s]: %(message)s',
'datefmt': '%m/%d/%Y %H:%M:%S',
'level': level,
'stream': sys.stdout
}
logging.basicConfig(**kw)
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(
logging.WARNING)
def get_process_cmd(pid, env=False):
env = 'e' if env else ''
try:
return subprocess.check_output('ps {} -o command {}'.format(env, pid),
shell=True).decode('utf8').strip().splitlines()[1]
# except Exception, e:
# print(e)
except:
return None
def get_match_pids(pattern):
pgrep_output = subprocess.check_output(
'pgrep -f "{}" || true'.format(pattern),
shell=True).decode('utf8').strip()
return [int(pid) for pid in pgrep_output.splitlines()]
def ask_for_confirm(msg):
confirm = click.prompt(msg, default='Y')
return confirm.lower() in ('y', 'yes')
def confirm_command_to_run(cmd):
if ask_for_confirm('Run the command: {} ?'.format(green(cmd))):
call(cmd)
else:
sys.exit(1)
def git_current_commit():
return get_command_output('git rev-parse --short HEAD').strip()
def get_command_output(cmd):
shell = not isinstance(cmd, list)
return subprocess.check_output(cmd, shell=shell).decode('utf8')
def ask_yes_or_no(msg, prompt='', default=None):
print('\n' + msg + '\n')
while True:
answer = input(prompt + ' [yes/no] ').lower()
if not answer:
continue
if answer not in ('yes', 'no', 'y', 'n'):
continue
if answer in ('yes', 'y'):
return True
else:
return False
def git_branch_exists(branch):
return call('git rev-parse --short --verify {}'.format(branch)) == 0
def to_unicode(s):
if isinstance(s, str):
return s.decode('utf-8')
else:
return s
def to_utf8(s):
if isinstance(s, unicode):
return s.encode('utf-8')
else:
return s
def git_commit_time(refspec):
return int(get_command_output('git log -1 --format="%ct" {}'.format(
refspec)).strip())
def get_seafile_version():
return os.environ['SEAFILE_VERSION']
def get_install_dir():
return join('/opt/seafile/' + get_conf('SEAFILE_SERVER', 'seafile-server') + '-{}'.format(get_seafile_version()))
def get_script(script):
return join(get_install_dir(), script)
_config = None
def get_conf(key, default=None):
key = key.upper()
return os.environ.get(key, default)
def _add_default_context(context):
default_context = {
'current_timestr': datetime.datetime.now().strftime('%m/%d/%Y %H:%M:%S'),
}
for k in default_context:
context.setdefault(k, default_context[k])
def render_template(template, target, context):
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader(dirname(template)))
_add_default_context(context)
content = env.get_template(basename(template)).render(**context)
with open(target, 'w') as fp:
fp.write(content)
def logdbg(msg):
if DEBUG_ENABLED:
msg = '[debug] ' + msg
loginfo(msg)
def loginfo(msg):
msg = '[{}] {}'.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), green(msg))
eprint(msg)
def cert_has_valid_days(cert, days):
assert exists(cert)
secs = 86400 * int(days)
retcode = call('openssl x509 -checkend {} -noout -in {}'.format(secs, cert), check_call=False)
return retcode == 0
def get_version_stamp_file():
return '/shared/seafile/seafile-data/current_version'
def read_version_stamp(fn=get_version_stamp_file()):
assert exists(fn), 'version stamp file {} does not exist!'.format(fn)
with open(fn, 'r') as fp:
return fp.read().strip()
def update_version_stamp(version, fn=get_version_stamp_file()):
with open(fn, 'w') as fp:
fp.write(version + '\n')
def wait_for_mysql():
db_host = get_conf('DB_HOST', '127.0.0.1')
db_user = 'root'
db_passwd = get_conf('DB_ROOT_PASSWD', '')
while True:
try:
pymysql.connect(host=db_host, port=3306, user=db_user, passwd=db_passwd)
except Exception as e:
print ('waiting for mysql server to be ready: %s', e)
time.sleep(2)
continue
logdbg('mysql server is ready')
return
def wait_for_nginx():
while True:
logdbg('waiting for nginx server to be ready')
output = get_command_output('netstat -nltp')
if ':80 ' in output:
logdbg(output)
logdbg('nginx is ready')
return
time.sleep(2)
def replace_file_pattern(fn, pattern, replacement):
with open(fn, 'r') as fp:
content = fp.read()
with open(fn, 'w') as fp:
fp.write(content.replace(pattern, replacement))