Merge pull request #2 from haiwen/support-upgrade

Support upgrade
This commit is contained in:
Shuai Lin 2016-11-29 09:22:59 +00:00 committed by GitHub
commit 145b0cc131
12 changed files with 517 additions and 72 deletions

View file

@ -42,6 +42,10 @@ sudo ./launcher restart
The logs are under `shared/logs/seafile`. The logs are under `shared/logs/seafile`.
#### Enable debug logs of launcher
Pass `-v` to launcher script (for example `sudo ./launcher -v rebuild` would make it print more verbose information
### Directory Structure ### Directory Structure
@ -115,13 +119,13 @@ If you want to use your own SSL certificate:
### Upgrading Seafile Server ### Upgrading Seafile Server
Simple run `./launcher rebuild`, which would keep your seafile server up to date. Simple run `sudo ./launcher rebuild`, which would keep your seafile server up to date.
### Troubleshooting ### Troubleshooting
View the container logs: `./launcher logs` View the container logs: `sudo ./launcher logs`
Spawn a shell inside your container using `./launcher enter`. This is the most foolproof method if you have host root access. Spawn a shell inside your container using `sudo ./launcher enter`. This is the most foolproof method if you have host root access.
### Developing with Vagrant ### Developing with Vagrant

View file

@ -11,9 +11,10 @@ pip install docker-squash
sudo cp samples/server.conf bootstrap/bootstrap.conf sudo cp samples/server.conf bootstrap/bootstrap.conf
sudo ./launcher bootstrap sudo ./launcher -v bootstrap
sudo ./launcher start && sleep 10 sudo ./launcher -v start && sleep 10
sudo ./launcher stop --skip-prereqs sudo ./launcher stop --skip-prereqs
sudo ./launcher start --docker-args "--memory 1g" && sleep 10 sudo ./launcher start --docker-args "--memory 1g" && sleep 10
sudo ./launcher restart sudo ./launcher restart
sudo ./launcher rebuild --docker-args "--memory 1g" sudo ./launcher -v rebuild
sudo ./launcher -v rebuild --docker-args "--memory 1g"

View file

@ -23,4 +23,11 @@ http {
include /etc/nginx/conf.d/*.conf; include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*; include /etc/nginx/sites-enabled/*;
server {
listen 80;
location / {
return 444;
}
}
} }

View file

@ -3,6 +3,11 @@ WORKDIR /opt/seafile
ENV SEAFILE_VERSION=6.0.5 ENV SEAFILE_VERSION=6.0.5
# syslog-ng and syslog-forwarder would mess up the container stdout, not good
# when debugging/upgrading.
RUN sed -i -e 's|\(^exec syslog-ng.*$\)|\1 >>/var/log/syslog-ng.log 2>\&1|g' /etc/service/syslog-ng/run && \
rm -rf /etc/service/syslog-forwarder
RUN mkdir -p /opt/seafile/ && \ RUN mkdir -p /opt/seafile/ && \
curl -sSL -o - https://bintray.com/artifact/download/seafile-org/seafile/seafile-server_6.0.5_x86-64.tar.gz \ curl -sSL -o - https://bintray.com/artifact/download/seafile-org/seafile/seafile-server_6.0.5_x86-64.tar.gz \
| tar xzf - -C /opt/seafile/ | tar xzf - -C /opt/seafile/

164
launcher
View file

@ -3,14 +3,14 @@
usage () { usage () {
echo "Usage: launcher COMMAND [--skip-prereqs] [--docker-args STRING]" echo "Usage: launcher COMMAND [--skip-prereqs] [--docker-args STRING]"
echo "Commands:" echo "Commands:"
echo " start: Start/initialize a container" echo " start: Start/initialize the container"
echo " stop: Stop a running container" echo " stop: Stop a running container"
echo " restart: Restart a container" echo " restart: Restart the container"
echo " destroy: Stop and remove a container" echo " destroy: Stop and remove the container"
echo " enter: Open a shell to run commands inside the container" echo " enter: Open a shell to run commands inside the container"
echo " logs: View the Docker logs for a container" echo " logs: View the Docker container logs"
echo " bootstrap: Bootstrap a container for the config based on a template" echo " bootstrap: Bootstrap the container based on a template"
echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)" echo " rebuild: Rebuild the container (destroy old, bootstrap, start new)"
echo echo
echo "Options:" echo "Options:"
echo " --skip-prereqs Don't check launcher prerequisites" echo " --skip-prereqs Don't check launcher prerequisites"
@ -28,16 +28,17 @@ dockerdir=$(cd "$(dirname $0)"; pwd -P)
sharedir=$dockerdir/shared sharedir=$dockerdir/shared
installdir=/opt/seafile/seafile-server-$version installdir=/opt/seafile/seafile-server-$version
bootstrap_conf=$dockerdir/bootstrap/bootstrap.conf bootstrap_conf=$dockerdir/bootstrap/bootstrap.conf
version_stamp_file=$sharedir/seafile/seafile-data/current_version
cd $dockerdir cd $dockerdir
dbg() { logdbg() {
if [[ $debug == "true" ]]; then if [[ $verbose == "true" ]]; then
echo "dbg: $1" loginfo "[debug] $1"
fi fi
} }
show_progress() { loginfo() {
if [[ -t 1 ]]; then if [[ -t 1 ]]; then
>&2 printf "[$(date +'%Y-%m-%d %H:%M:%S')] \033[32m%s\033[m\n" "$1" >&2 printf "[$(date +'%Y-%m-%d %H:%M:%S')] \033[32m%s\033[m\n" "$1"
else else
@ -75,6 +76,13 @@ init_shared() {
fi fi
} }
set_envs() {
envs=""
if [[ $verbose == "true" ]]; then
envs="$envs -e SEAFILE_DOCKER_VERBOSE=true"
fi
}
set_ports() { set_ports() {
ports=$(docker run $user_args --rm -it \ ports=$(docker run $user_args --rm -it \
-v ${dockerdir}/scripts:/scripts \ -v ${dockerdir}/scripts:/scripts \
@ -131,34 +139,37 @@ bootstrap() {
fi fi
docker history $image >/dev/null 2>&1 || { docker history $image >/dev/null 2>&1 || {
show_progress "Pulling Seafile server image $version, this may take a while." loginfo "Pulling Seafile server image $version, this may take a while."
docker pull $image docker pull $image
show_progress "Seafile server image $version pulled. Now bootstrapping the server ..." loginfo "Seafile server image $version pulled. Now bootstrapping the server ..."
} }
# First initialize seafile server and letsencrypt # First initialize seafile server and letsencrypt
set_envs
set_bootstrap_volumes set_bootstrap_volumes
set_ports set_ports
docker run $user_args --rm -it --name seafile-bootstrap -e SEAFILE_BOOTSRAP=1 $volumes $ports $image /sbin/my_init -- /scripts/bootstrap.py docker run $user_args --rm -it --name seafile-bootstrap -e SEAFILE_BOOTSRAP=1 $envs $volumes $ports $image /sbin/my_init $quiet -- /scripts/bootstrap.py
show_progress "Now building the local docker image." loginfo "Now building the local docker image."
docker build -f bootstrap/generated/Dockerfile -t local_seafile/server:latest . >/dev/null docker build -f bootstrap/generated/Dockerfile -t local_seafile/server:latest . >/dev/null
show_progress "Image built." loginfo "Image built."
} }
start() { start() {
existing=$(docker ps | awk '{ print $1, $(NF) }' | grep " seafile$" | awk '{ print $1 }' || true) existing=$(docker ps | awk '{ print $1, $(NF) }' | grep " seafile$" | awk '{ print $1 }' || true)
if [[ $existing != "" ]]; then if [[ $existing != "" ]]; then
show_progress "Nothing to do, your container has already started!" loginfo "Nothing to do, your container has already started!"
exit 0 exit 0
fi fi
check_version_match
chmod 0700 $dockerdir/bootstrap $sharedir/seafile/conf chmod 0700 $dockerdir/bootstrap $sharedir/seafile/conf
set_existing_container set_existing_container
if [[ $existing != "" ]]; then if [[ $existing != "" ]]; then
show_progress "starting up existing container" loginfo "starting up existing container"
( (
set -x set -x
docker start seafile docker start seafile
@ -166,6 +177,7 @@ start() {
exit 0 exit 0
fi fi
set_envs
set_volumes set_volumes
set_ports set_ports
@ -177,10 +189,10 @@ start() {
attach_on_run="-d" attach_on_run="-d"
fi fi
show_progress "Starting up new seafile server container" loginfo "Starting up new seafile server container"
( (
set -x set -x
docker run $user_args $attach_on_run $restart_policy --name seafile -h seafile $volumes $ports $local_image docker run $user_args $attach_on_run $restart_policy --name seafile -h seafile $envs $volumes $ports $local_image
) )
} }
@ -240,9 +252,74 @@ destroy() {
) )
} }
get_major_version() {
echo $1| awk -F . '{printf "%s.%s", $1, $2}'
}
check_version_match() {
local last_version last_major_version current_major_version
last_version=$(cat $version_stamp_file)
last_major_version=$(get_major_version $last_version)
current_major_version=$(get_major_version $version)
logdbg "Your version: ${last_version}, latest version: ${version}"
if [[ $last_major_version != "$current_major_version" ]]; then
loginfo "******* Major upgrade detected *******"
loginfo "You have $last_version, latest is $version"
loginfo "Please run './launcher rebuild' to upgrade"
exit 1
fi
}
check_upgrade() {
loginfo "Checking if there is major version upgrade"
local last_version last_major_version current_major_version
last_version=$(cat $version_stamp_file)
last_major_version=$(get_major_version $last_version)
current_major_version=$(get_major_version $version)
if [[ $last_major_version == "$current_major_version" ]]; then
return
else
loginfo "********************************"
loginfo "Major upgrade detected: You have $last_version, latest is $version"
loginfo "********************************"
# use_manual_upgrade=true
if [[ $use_manual_upgrade == "true" ]]; then
loginfo "Now you can run './launcher manual-upgrade' to do manual upgrade."
exit 0
else
loginfo "Going to launch the docker container for manual upgrade"
_launch_for_upgrade --auto
fi
fi
}
_launch_for_upgrade() {
local cmd
if [[ $1 == "--auto" ]]; then
cmd="/scripts/upgrade.py"
else
cmd="/bin/bash"
fi
set_envs
set_volumes
(
set -x
docker run $user_args \
-it --rm --name seafile-upgrade -h seafile \
$envs $volumes $local_image \
/sbin/my_init $quiet -- $cmd
)
}
rebuild() { rebuild() {
if [[ "$(git symbolic-ref --short HEAD)" == "master" ]]; then if [[ "$(git symbolic-ref --short HEAD)" == "master" ]]; then
show_progress "Ensuring launcher is up to date" loginfo "Ensuring launcher is up to date"
git remote update git remote update
@ -251,10 +328,10 @@ rebuild() {
BASE=$(git merge-base @ "@{u}") BASE=$(git merge-base @ "@{u}")
if [[ $LOCAL = "$REMOTE" ]]; then if [[ $LOCAL = "$REMOTE" ]]; then
show_progress "Launcher is up-to-date" loginfo "Launcher is up-to-date"
elif [[ $LOCAL = "$BASE" ]]; then elif [[ $LOCAL = "$BASE" ]]; then
show_progress "Updating Launcher" loginfo "Updating Launcher"
git pull || (echo 'failed to update' && exit 1) git pull || (echo 'failed to update' && exit 1)
for (( i=${#BASH_ARGV[@]}-1,j=0; i>=0,j<${#BASH_ARGV[@]}; i--,j++ )) for (( i=${#BASH_ARGV[@]}-1,j=0; i>=0,j<${#BASH_ARGV[@]}; i--,j++ ))
@ -264,9 +341,9 @@ rebuild() {
exec /bin/bash $0 "${args[@]}" # $@ is empty, because of shift at the beginning. Use BASH_ARGV instead. exec /bin/bash $0 "${args[@]}" # $@ is empty, because of shift at the beginning. Use BASH_ARGV instead.
elif [[ $REMOTE = "$BASE" ]]; then elif [[ $REMOTE = "$BASE" ]]; then
show_progress "Your version of Launcher is ahead of origin" loginfo "Your version of Launcher is ahead of origin"
else else
show_progress "Launcher has diverged source, this is only expected in Dev mode" loginfo "Launcher has diverged source, this is only expected in Dev mode"
fi fi
fi fi
@ -274,7 +351,7 @@ rebuild() {
set_existing_container set_existing_container
if [[ $existing != "" ]]; then if [[ $existing != "" ]]; then
show_progress "Stopping old container" loginfo "Stopping old container"
( (
set -x set -x
docker stop -t 10 seafile docker stop -t 10 seafile
@ -282,35 +359,56 @@ rebuild() {
fi fi
bootstrap bootstrap
show_progress "Rebuilt successfully." loginfo "Rebuilt successfully."
if [[ $existing != "" ]]; then if [[ $existing != "" ]]; then
show_progress "Removing old container" loginfo "Removing old container"
( (
set -x set -x
docker rm seafile docker rm seafile
) )
fi fi
check_upgrade
start start
show_progress "Your seafile server is now running." loginfo "Your seafile server is now running."
exit 0 exit 0
} }
manual_upgrade() {
_launch_for_upgrade
loginfo "If you have manually upgraded the server, please update the version stamp by:"
loginfo
loginfo " echo $version | sudo tee $version_stamp_file"
loginfo " sudo ./launcher start"
loginfo
}
main() { main() {
local action local action
while [[ $# -gt 0 ]] while [[ $# -gt 0 ]]
do do
case "$1" in case "$1" in
bootstrap|start|stop|restart|enter|destroy|logs|rebuild) bootstrap|start|stop|restart|enter|destroy|logs|rebuild|manual-upgrade)
action=$1 ; shift 1 ;; action=${1//-/_} ; shift 1 ;;
--debug) debug=true ; shift 1 ;; -h|--help) ( usage ; exit 1) ; shift 1 ;;
--skip-prereqs) SKIP_PREREQS=true ; shift 1 ;; -v|--verbose) verbose=true ; shift 1 ;;
--docker-args) user_args=$2 ; shift 2 ;; --skip-prereqs) SKIP_PREREQS=true ; shift 1 ;;
--docker-args) user_args=$2 ; shift 2 ;;
--manual-upgrade) use_manual_upgrade=true ; shift 1 ;;
*) err_and_quit "Argument error. Please see help." ;; *) err_and_quit "Argument error. Please see help." ;;
esac esac
done done
# By default we suppress the verbose logs like "Running
# /etc/my_init.d/99_mysql_setup.sh". Use "./launcher -v <cmd>" to enable
# printing of these verbose logs.
quiet="--quiet"
if [[ $verbose == "true" ]]; then
quiet=""
fi
"$action" "$action"
} }

View file

@ -2,9 +2,7 @@
#coding: UTF-8 #coding: UTF-8
""" """
This script calls the appropriate seafile init scripts (e.g. Bootstraping seafile server, letsencrypt (verification & cron job).
setup-seafile.sh or setup-seafile-mysql.sh. It's supposed to run inside the
container.
""" """
import argparse import argparse
@ -16,9 +14,10 @@ import uuid
import time import time
from utils import ( from utils import (
call, get_conf, get_install_dir, show_progress, call, get_conf, get_install_dir, loginfo,
get_script, render_template, get_seafile_version, eprint, get_script, render_template, get_seafile_version, eprint,
cert_has_valid_days, get_version_stamp_file, update_version_stamp cert_has_valid_days, get_version_stamp_file, update_version_stamp,
wait_for_mysql, wait_for_nginx
) )
seafile_version = get_seafile_version() seafile_version = get_seafile_version()
@ -29,12 +28,13 @@ ssl_dir = '/shared/ssl'
generated_dir = '/bootstrap/generated' generated_dir = '/bootstrap/generated'
def init_letsencrypt(): def init_letsencrypt():
show_progress('Preparing for letsencrypt ...') loginfo('Preparing for letsencrypt ...')
wait_for_nginx()
if not exists(ssl_dir): if not exists(ssl_dir):
os.mkdir(ssl_dir) os.mkdir(ssl_dir)
domain = get_conf('server.hostname') domain = get_conf('server.hostname')
context = { context = {
'ssl_dir': ssl_dir, 'ssl_dir': ssl_dir,
'domain': domain, 'domain': domain,
@ -47,12 +47,12 @@ def init_letsencrypt():
ssl_crt = '/shared/ssl/{}.crt'.format(domain) ssl_crt = '/shared/ssl/{}.crt'.format(domain)
if exists(ssl_crt): if exists(ssl_crt):
show_progress('Found existing cert file {}'.format(ssl_crt)) loginfo('Found existing cert file {}'.format(ssl_crt))
if cert_has_valid_days(ssl_crt, 30): if cert_has_valid_days(ssl_crt, 30):
show_progress('Skip letsencrypt verification since we have a valid certificate') loginfo('Skip letsencrypt verification since we have a valid certificate')
return return
show_progress('Starting letsencrypt verification') loginfo('Starting letsencrypt verification')
# Create a temporary nginx conf to start a server, which would accessed by letsencrypt # Create a temporary nginx conf to start a server, which would accessed by letsencrypt
context = { context = {
'https': False, 'https': False,
@ -61,10 +61,8 @@ def init_letsencrypt():
render_template('/templates/seafile.nginx.conf.template', render_template('/templates/seafile.nginx.conf.template',
'/etc/nginx/sites-enabled/seafile.nginx.conf', context) '/etc/nginx/sites-enabled/seafile.nginx.conf', context)
# TODO: The 5 seconds heuristic is not good, how can we know for sure nginx is ready?
print 'waiting for nginx server to be ready'
time.sleep(5)
call('nginx -s reload') call('nginx -s reload')
time.sleep(2)
call('/scripts/ssl.sh {0} {1}'.format(ssl_dir, domain)) 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: # if call('/scripts/ssl.sh {0} {1}'.format(ssl_dir, domain), check_call=False) != 0:
@ -91,7 +89,7 @@ def is_https():
return get_conf('server.letsencrypt', '').lower() == 'true' return get_conf('server.letsencrypt', '').lower() == 'true'
def generate_local_dockerfile(): def generate_local_dockerfile():
show_progress('Generating local Dockerfile ...') loginfo('Generating local Dockerfile ...')
context = { context = {
'seafile_version': seafile_version, 'seafile_version': seafile_version,
'https': is_https(), 'https': is_https(),
@ -121,10 +119,10 @@ def init_seafile_server():
if exists(join(shared_seafiledir, 'seafile-data')): if exists(join(shared_seafiledir, 'seafile-data')):
if not exists(version_stamp_file): if not exists(version_stamp_file):
update_version_stamp(version_stamp_file, os.environ['SEAFILE_VERSION']) update_version_stamp(version_stamp_file, os.environ['SEAFILE_VERSION'])
show_progress('Skip running setup-seafile-mysql.py because there is existing seafile-data folder.') loginfo('Skip running setup-seafile-mysql.py because there is existing seafile-data folder.')
return return
show_progress('Now running setup-seafile-mysql.py in auto mode.') loginfo('Now running setup-seafile-mysql.py in auto mode.')
env = { env = {
'SERVER_NAME': 'seafile', 'SERVER_NAME': 'seafile',
'SERVER_IP': get_conf('server.hostname'), 'SERVER_IP': get_conf('server.hostname'),
@ -156,7 +154,8 @@ def init_seafile_server():
if not exists(dst) and exists(src): if not exists(dst) and exists(src):
shutil.move(src, shared_seafiledir) shutil.move(src, shared_seafiledir)
update_version_stamp(version_stamp_file, os.environ['SEAFILE_VERSION']) loginfo('Updating version stamp')
update_version_stamp(os.environ['SEAFILE_VERSION'])
def main(): def main():
args = parse_args() args = parse_args()
@ -174,9 +173,10 @@ def main():
init_letsencrypt() init_letsencrypt()
generate_local_nginx_conf() generate_local_nginx_conf()
wait_for_mysql()
init_seafile_server() init_seafile_server()
show_progress('Generated local config.') loginfo('Generated local config.')
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -2,9 +2,8 @@
#coding: UTF-8 #coding: UTF-8
""" """
This script calls the appropriate seafile init scripts (e.g. Starts the seafile/seahub server and watches the controller process. It is
setup-seafile.sh or setup-seafile-mysql.sh. It's supposed to run inside the the entrypoint command of the docker container.
container.
""" """
import json import json
@ -14,7 +13,10 @@ import shutil
import sys import sys
import time import time
from utils import call, get_conf, get_install_dir, get_script, get_command_output, render_template from utils import (
call, get_conf, get_install_dir, get_script, get_command_output,
render_template, wait_for_mysql
)
installdir = get_install_dir() installdir = get_install_dir()
topdir = dirname(installdir) topdir = dirname(installdir)
@ -26,7 +28,9 @@ def watch_controller():
controller_pid = get_command_output('ps aux | grep seafile-controller |grep -v grep || true').strip() controller_pid = get_command_output('ps aux | grep seafile-controller |grep -v grep || true').strip()
if not controller_pid: if not controller_pid:
retry += 1 retry += 1
time.sleep(2) else:
retry = 0
time.sleep(5)
print 'seafile controller exited unexpectedly.' print 'seafile controller exited unexpectedly.'
sys.exit(1) sys.exit(1)
@ -39,10 +43,7 @@ def main():
with open(password_file, 'w') as fp: with open(password_file, 'w') as fp:
json.dump(admin_pw, fp) json.dump(admin_pw, fp)
while not exists('/var/run/mysqld/mysqld.sock'): wait_for_mysql()
print 'waiting for mysql server to be ready'
time.sleep(2)
print 'mysql server is ready'
try: try:
call('{} start'.format(get_script('seafile.sh'))) call('{} start'.format(get_script('seafile.sh')))

80
scripts/upgrade.py Executable file
View file

@ -0,0 +1,80 @@
#!/usr/bin/env python
#coding: UTF-8
"""
This script is used to run proper upgrade scripts automatically.
"""
import json
import re
import glob
import os
from os.path import abspath, basename, exists, dirname, join, isdir
import shutil
import sys
import time
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)
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 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 check_upgrade():
last_version = read_version_stamp()
current_version = os.environ['SEAFILE_VERSION']
if last_version == current_version:
return
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))
new_version = parse_upgrade_script_version(script)[1] + '.0'
replace_file_pattern(script, 'read dummy', '')
call(script)
update_version_stamp(new_version)
update_version_stamp(current_version)
def main():
wait_for_mysql()
os.chdir(installdir)
check_upgrade()
if __name__ == '__main__':
main()

View file

@ -9,6 +9,7 @@ from os.path import abspath, basename, exists, dirname, join, isdir, expanduser
import platform import platform
import sys import sys
import subprocess import subprocess
import time
import logging import logging
import logging.config import logging.config
import click import click
@ -17,6 +18,8 @@ import colorlog
logger = logging.getLogger('.utils') logger = logging.getLogger('.utils')
DEBUG_ENABLED = os.environ.get('SEAFILE_DOCKER_VERBOSE', '').lower() in ('true', '1', 'yes')
def eprint(*a, **kw): def eprint(*a, **kw):
kw['file'] = sys.stderr kw['file'] = sys.stderr
print(*a, **kw) print(*a, **kw)
@ -47,7 +50,7 @@ def _find_flag(args, *opts, **kw):
def call(*a, **kw): def call(*a, **kw):
dry_run = kw.pop('dry_run', False) dry_run = kw.pop('dry_run', False)
quiet = kw.pop('quiet', False) quiet = kw.pop('quiet', DEBUG_ENABLED)
cwd = kw.get('cwd', os.getcwd()) cwd = kw.get('cwd', os.getcwd())
check_call = kw.pop('check_call', True) check_call = kw.pop('check_call', True)
reduct_args = kw.pop('reduct_args', []) reduct_args = kw.pop('reduct_args', [])
@ -57,8 +60,8 @@ def call(*a, **kw):
for arg in reduct_args: for arg in reduct_args:
value = _find_flag(args, arg) value = _find_flag(args, arg)
toprint = toprint.replace(value, '{}**reducted**'.format(value[:3])) toprint = toprint.replace(value, '{}**reducted**'.format(value[:3]))
eprint('calling: ', green(toprint)) logdbg('calling: ' + green(toprint))
eprint('cwd: ', green(cwd)) logdbg('cwd: ' + green(cwd))
kw.setdefault('shell', True) kw.setdefault('shell', True)
if not dry_run: if not dry_run:
if check_call: if check_call:
@ -237,7 +240,12 @@ def render_template(template, target, context):
with open(target, 'w') as fp: with open(target, 'w') as fp:
fp.write(content) fp.write(content)
def show_progress(msg): 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)) msg = '[{}] {}'.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), green(msg))
eprint(msg) eprint(msg)
@ -251,11 +259,33 @@ def cert_has_valid_days(cert, days):
def get_version_stamp_file(): def get_version_stamp_file():
return '/shared/seafile/seafile-data/current_version' return '/shared/seafile/seafile-data/current_version'
def read_version_stamp(fn): def read_version_stamp(fn=get_version_stamp_file()):
assert exists(fn), 'version stamp file {} does not exist!'.format(fn) assert exists(fn), 'version stamp file {} does not exist!'.format(fn)
with open(fn, 'r') as fp: with open(fn, 'r') as fp:
return fp.read().strip() return fp.read().strip()
def update_version_stamp(fn, version): def update_version_stamp(version, fn=get_version_stamp_file()):
with open(fn, 'w') as fp: with open(fn, 'w') as fp:
fp.write(version + '\n') fp.write(version + '\n')
def wait_for_mysql():
while not exists('/var/run/mysqld/mysqld.sock'):
logdbg('waiting for mysql server to be ready')
time.sleep(2)
logdbg('mysql server is ready')
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))

7
tests/Dockerfile Normal file
View file

@ -0,0 +1,7 @@
FROM seafileorg/server:6.0.5
ENV SEAFILE_VERSION=6.1.0
RUN mv /opt/seafile/seafile-server-6.0.5 /opt/seafile/seafile-server-${SEAFILE_VERSION}
ADD upgrade_6.0_6.1.sh /opt/seafile/seafile-server-${SEAFILE_VERSION}/upgrade/upgrade_6.0_6.1.sh

2
tests/Makefile Normal file
View file

@ -0,0 +1,2 @@
all:
docker build -t seafileorg/server:6.1.0 .

210
tests/upgrade_6.0_6.1.sh Executable file
View file

@ -0,0 +1,210 @@
#!/bin/bash
SCRIPT=$(readlink -f "$0") # haiwen/seafile-server-1.3.0/upgrade/upgrade_xx_xx.sh
UPGRADE_DIR=$(dirname "$SCRIPT") # haiwen/seafile-server-1.3.0/upgrade/
INSTALLPATH=$(dirname "$UPGRADE_DIR") # haiwen/seafile-server-1.3.0/
TOPDIR=$(dirname "${INSTALLPATH}") # haiwen/
default_ccnet_conf_dir=${TOPDIR}/ccnet
default_conf_dir=${TOPDIR}/conf
seafile_server_symlink=${TOPDIR}/seafile-server-latest
seahub_data_dir=${TOPDIR}/seahub-data
seahub_settings_py=${TOPDIR}/seahub_settings.py
manage_py=${INSTALLPATH}/seahub/manage.py
export CCNET_CONF_DIR=${default_ccnet_conf_dir}
export SEAFILE_CENTRAL_CONF_DIR=${default_conf_dir}
export PYTHONPATH=${INSTALLPATH}/seafile/lib/python2.6/site-packages:${INSTALLPATH}/seafile/lib64/python2.6/site-packages:${INSTALLPATH}/seafile/lib/python2.7/site-packages:${INSTALLPATH}/seahub/thirdpart:$PYTHONPATH
export PYTHONPATH=${INSTALLPATH}/seafile/lib/python2.7/site-packages:${INSTALLPATH}/seafile/lib64/python2.7/site-packages:$PYTHONPATH
export SEAFILE_LD_LIBRARY_PATH=${INSTALLPATH}/seafile/lib/:${INSTALLPATH}/seafile/lib64:${LD_LIBRARY_PATH}
prev_version=6.0
current_version=6.1
echo
echo "-------------------------------------------------------------"
echo "This script would upgrade your seafile server from ${prev_version} to ${current_version}"
echo "Press [ENTER] to contiune"
echo "-------------------------------------------------------------"
echo
read dummy
function check_python_executable() {
if [[ "$PYTHON" != "" && -x $PYTHON ]]; then
return 0
fi
if which python2.7 2>/dev/null 1>&2; then
PYTHON=python2.7
elif which python27 2>/dev/null 1>&2; then
PYTHON=python27
else
echo
echo "Can't find a python executable of version 2.7 or above in PATH"
echo "Install python 2.7+ before continue."
echo "Or if you installed it in a non-standard PATH, set the PYTHON enviroment varirable to it"
echo
exit 1
fi
}
function read_seafile_data_dir () {
seafile_ini=${default_ccnet_conf_dir}/seafile.ini
if [[ ! -f ${seafile_ini} ]]; then
echo "${seafile_ini} not found. Now quit"
exit 1
fi
seafile_data_dir=$(cat "${seafile_ini}")
if [[ ! -d ${seafile_data_dir} ]]; then
echo "Your seafile server data directory \"${seafile_data_dir}\" is invalid or doesn't exits."
echo "Please check it first, or create this directory yourself."
echo ""
exit 1;
fi
export SEAFILE_CONF_DIR=$seafile_data_dir
}
function ensure_server_not_running() {
# test whether seafile server has been stopped.
if pgrep seaf-server 2>/dev/null 1>&2 ; then
echo
echo "seafile server is still running !"
echo "stop it using scripts before upgrade."
echo
exit 1
elif pgrep -f "${manage_py} run_gunicorn" 2>/dev/null 1>&2 \
|| pgrep -f "seahub.wsgi:application" 2>/dev/null 1>&2; then
echo
echo "seahub server is still running !"
echo "stop it before upgrade."
echo
exit 1
elif pgrep -f "${manage_py} runfcgi" 2>/dev/null 1>&2 ; then
echo
echo "seahub server is still running !"
echo "stop it before upgrade."
echo
exit 1
fi
}
function migrate_avatars() {
echo
echo "migrating avatars ..."
echo
media_dir=${INSTALLPATH}/seahub/media
orig_avatar_dir=${INSTALLPATH}/seahub/media/avatars
dest_avatar_dir=${TOPDIR}/seahub-data/avatars
# move "media/avatars" directory outside
if [[ ! -d ${dest_avatar_dir} ]]; then
mkdir -p "${TOPDIR}/seahub-data"
mv "${orig_avatar_dir}" "${dest_avatar_dir}" 2>/dev/null 1>&2
ln -s ../../../seahub-data/avatars "${media_dir}"
elif [[ ! -L ${orig_avatar_dir} ]]; then
mv "${orig_avatar_dir}"/* "${dest_avatar_dir}" 2>/dev/null 1>&2
rm -rf "${orig_avatar_dir}"
ln -s ../../../seahub-data/avatars "${media_dir}"
fi
echo "Done"
}
function update_database() {
echo
echo "Updating seafile/seahub database ..."
echo
db_update_helper=${UPGRADE_DIR}/db_update_helper.py
if ! $PYTHON "${db_update_helper}" 6.0.0; then
echo
echo "Failed to upgrade your database"
echo
exit 1
fi
echo "Done"
}
function upgrade_seafile_server_latest_symlink() {
# update the symlink seafile-server to the new server version
if [[ -L "${seafile_server_symlink}" || ! -e "${seafile_server_symlink}" ]]; then
echo
printf "updating \033[33m${seafile_server_symlink}\033[m symbolic link to \033[33m${INSTALLPATH}\033[m ...\n\n"
echo
if ! rm -f "${seafile_server_symlink}"; then
echo "Failed to remove ${seafile_server_symlink}"
echo
exit 1;
fi
if ! ln -s "$(basename ${INSTALLPATH})" "${seafile_server_symlink}"; then
echo "Failed to update ${seafile_server_symlink} symbolic link."
echo
exit 1;
fi
fi
}
function make_media_custom_symlink() {
media_symlink=${INSTALLPATH}/seahub/media/custom
if [[ -L "${media_symlink}" ]]; then
return
elif [[ ! -e "${media_symlink}" ]]; then
ln -s ../../../seahub-data/custom "${media_symlink}"
return
elif [[ -d "${media_symlink}" ]]; then
cp -rf "${media_symlink}" "${seahub_data_dir}/"
rm -rf "${media_symlink}"
ln -s ../../../seahub-data/custom "${media_symlink}"
fi
}
function move_old_customdir_outside() {
# find the path of the latest seafile server folder
if [[ -L ${seafile_server_symlink} ]]; then
latest_server=$(readlink -f "${seafile_server_symlink}")
else
return
fi
old_customdir=${latest_server}/seahub/media/custom
# old customdir is already a symlink, do nothing
if [[ -L "${old_customdir}" ]]; then
return
fi
# old customdir does not exist, do nothing
if [[ ! -e "${old_customdir}" ]]; then
return
fi
# media/custom exist and is not a symlink
cp -rf "${old_customdir}" "${seahub_data_dir}/"
}
#################
# The main execution flow of the script
################
check_python_executable;
read_seafile_data_dir;
ensure_server_not_running;
# update_database;
migrate_avatars;
move_old_customdir_outside;
make_media_custom_symlink;
upgrade_seafile_server_latest_symlink;
echo
echo "-----------------------------------------------------------------"
echo "Upgraded your seafile server successfully."
echo "-----------------------------------------------------------------"
echo