seafile-containerized/launcher
2018-01-13 21:33:46 +08:00

433 lines
11 KiB
Bash
Executable file

#!/bin/bash
usage () {
echo "Usage: launcher COMMAND [--skip-prereqs] [--docker-args STRING]"
echo "Commands:"
echo " start: Start/initialize the container"
echo " stop: Stop a running container"
echo " restart: Restart the container"
echo " destroy: Stop and remove the container"
echo " enter: Open a shell to run commands inside the container"
echo " logs: View the Docker container logs"
echo " bootstrap: Bootstrap the container based on a template"
echo " rebuild: Rebuild the container (destroy old, bootstrap, start new)"
echo " gc: Start the seafile garbage collector (stops seafile, starts gc, restarts seafile)"
echo
echo "Options:"
echo " --skip-prereqs Don't check launcher prerequisites"
echo " --docker-args Extra arguments to pass when running docker"
exit 1
}
set -e
set -o pipefail
version=6.2.3
image=seafileltd/seafile:$version
local_image=local_seafile/server:latest
dockerdir=$(cd "$(dirname $0)"; pwd -P)
sharedir=$dockerdir/shared
installdir=/opt/seafile/seafile-server-$version
bootstrap_conf=$dockerdir/bootstrap/bootstrap.conf
version_stamp_file=$sharedir/seafile/seafile-data/current_version
cd $dockerdir
logdbg() {
if [[ $verbose == "true" ]]; then
loginfo "[debug] $1"
fi
}
loginfo() {
if [[ -t 1 ]]; then
>&2 printf "[$(date +'%Y-%m-%d %H:%M:%S')] \033[32m%s\033[m\n" "$1"
else
>&2 echo "[$(date +'%Y-%m-%d %H:%M:%S')] " "$1"
fi
}
install_docker() {
echo "---------------------------------------------------------------------------------"
echo "Docker is not installed, you will need to install Docker in order to run Launcher"
echo "See https://docs.docker.com/installation/"
echo "---------------------------------------------------------------------------------"
exit 1
}
err_and_quit () {
if [[ -t 1 ]]; then
>&2 printf "\n\n\033[33mError: %s\033[m\n\n" "$1"
else
>&2 echo "$1"
fi
exit 1
}
init_shared() {
mkdir -p $sharedir/{seafile,db}
mkdir -p $sharedir/logs/{seafile,var-log}
mkdir -p $sharedir/logs/var-log/nginx
touch $sharedir/logs/var-log/syslog
local bash_history=$sharedir/.bash_history
if [[ ! -e $bash_history ]]; then
touch $bash_history
fi
}
set_envs() {
envs=""
if [[ $verbose == "true" ]]; then
envs="$envs -e SEAFILE_DOCKER_VERBOSE=true"
fi
}
set_ports() {
ports=$(docker run $user_args --rm -it \
-v ${dockerdir}/scripts:/scripts \
-v ${dockerdir}/bootstrap:/bootstrap:ro \
$image \
/scripts/bootstrap.py --parse-ports)
}
set_bootstrap_volumes() {
local mounts
init_shared
mounts=(
$sharedir:/shared
$sharedir/logs/var-log:/var/log
$sharedir/db:/var/lib/mysql
$dockerdir/bootstrap:/bootstrap
$dockerdir/scripts:/scripts:ro
$dockerdir/templates:/templates:ro
$dockerdir/scripts/tmp/check_init_admin.py:$installdir/check_init_admin.py:ro
$sharedir/.bash_history:/root/.bash_history
)
volumes=""
local m
for m in ${mounts[*]}; do
volumes="$volumes -v $m"
done
}
set_volumes() {
local mounts
init_shared
mounts=(
$sharedir:/shared
$sharedir/logs/var-log:/var/log
$sharedir/db:/var/lib/mysql
$sharedir/.bash_history:/root/.bash_history
)
volumes=""
local m
for m in ${mounts[*]}; do
volumes="$volumes -v $m"
done
}
set_existing_container() {
existing=$(docker ps -a | awk '{ print $1, $(NF) }' | grep " seafile$" | awk '{ print $1 }' || true)
}
bootstrap() {
if [[ ! -e $bootstrap_conf ]]; then
err_and_quit "The file $bootstrap_conf doesn't exist. Have you run seafile-server-setup?"
fi
docker history $image >/dev/null 2>&1 || {
loginfo "Pulling Seafile server image $version, this may take a while."
docker pull $image
loginfo "Seafile server image $version pulled. Now bootstrapping the server ..."
}
# First initialize seafile server and letsencrypt
set_envs
set_bootstrap_volumes
set_ports
docker run $user_args --rm -it --name seafile-bootstrap -e SEAFILE_BOOTSRAP=1 $envs $volumes $ports $image /sbin/my_init $quiet -- /scripts/bootstrap.py
loginfo "Now building the local docker image."
docker build -f bootstrap/generated/Dockerfile -t local_seafile/server:latest . >/dev/null
loginfo "Image built."
}
start() {
existing=$(docker ps | awk '{ print $1, $(NF) }' | grep " seafile$" | awk '{ print $1 }' || true)
if [[ $existing != "" ]]; then
loginfo "Nothing to do, your container has already started!"
exit 0
fi
check_version_match
chmod 0700 $dockerdir/bootstrap $sharedir/seafile/conf
set_existing_container
if [[ $existing != "" ]]; then
loginfo "starting up existing container"
(
set -x
docker start seafile
)
exit 0
fi
set_envs
set_volumes
set_ports
local restart_policy attach_on_run
if [[ "${SUPERVISED}" = "true" ]]; then
restart_policy="--restart=no"
attach_on_run="-a stdout -a stderr"
else
attach_on_run="-d"
fi
loginfo "Starting up new seafile server container"
(
set -x
docker run $user_args $attach_on_run $restart_policy --name seafile -h seafile $envs $volumes $ports $local_image
)
}
ensure_container_running() {
set_existing_container
if [[ $existing == "" ]]; then
err_and_quit "seafile was not started !"
fi
}
stop() {
ensure_container_running
(
set -x
docker stop -t 10 seafile
)
}
enter() {
ensure_container_running
(
set -x
docker exec -it seafile /bin/bash
)
}
restart() {
stop
start
}
gc() {
ensure_container_running
(
loginfo "Stop seafile and start the garbage collector"
set +e +x
docker exec -it seafile /scripts/gc.sh
gc_error=$?
if [[ $gc_error -eq 0 ]]; then
loginfo "Garbage collector finished!"
else
err_and_quit "Garbage collector exited with code $gc_error"
fi
)
}
check_prereqs() {
if [[ $SKIP_PREREQS == "true" ]]; then
return 0
fi
# check docker
if ! which docker >/dev/null; then
install_docker
fi
# TODO: check git version
}
logs() {
ensure_container_running
(
set -x
docker logs --tail=20 -f seafile
)
}
destroy() {
(
set -x
docker stop -t 10 seafile || true
docker rm seafile
)
}
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() {
if [[ "$(git symbolic-ref --short HEAD)" == "master" ]]; then
loginfo "Ensuring launcher is up to date"
git remote update
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "@{u}")
BASE=$(git merge-base @ "@{u}")
if [[ $LOCAL = "$REMOTE" ]]; then
loginfo "Launcher is up-to-date"
elif [[ $LOCAL = "$BASE" ]]; then
loginfo "Updating Launcher"
git pull || (echo 'failed to update' && exit 1)
for (( i=${#BASH_ARGV[@]}-1,j=0; i>=0,j<${#BASH_ARGV[@]}; i--,j++ ))
do
args[$j]=${BASH_ARGV[$i]}
done
exec /bin/bash $0 "${args[@]}" # $@ is empty, because of shift at the beginning. Use BASH_ARGV instead.
elif [[ $REMOTE = "$BASE" ]]; then
loginfo "Your version of Launcher is ahead of origin"
else
loginfo "Launcher has diverged source, this is only expected in Dev mode"
fi
fi
set_existing_container
if [[ $existing != "" ]]; then
loginfo "Stopping old container"
(
set -x
docker stop -t 10 seafile
)
fi
bootstrap
loginfo "Rebuilt successfully."
if [[ $existing != "" ]]; then
loginfo "Removing old container"
(
set -x
docker rm seafile
)
fi
check_upgrade
start
loginfo "Your seafile server is now running."
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() {
local action
while [[ $# -gt 0 ]]
do
case "$1" in
bootstrap|start|stop|restart|enter|destroy|logs|rebuild|manual-upgrade|gc)
action=${1//-/_} ; shift 1 ;;
-h|--help) ( usage ; exit 1) ; shift 1 ;;
-v|--verbose) verbose=true ; shift 1 ;;
--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." ;;
esac
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"
}
check_prereqs
main "$@"