Skip to content

Commit

Permalink
Issue #203: a lot of shell scripting for patchlevel upgrades
Browse files Browse the repository at this point in the history
Also use exec consistently.
Also support fallback for running other commands.
  • Loading branch information
bschmalhofer committed Jul 24, 2020
1 parent bd26009 commit 620a61f
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 33 deletions.
159 changes: 134 additions & 25 deletions bin/docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,35 +1,73 @@
#!/usr/bin/env bash

# root handles 'cron' and defers 'web' to the OTOBO user
# Also check whether $UID is set, as 'exec su user ...' apparently does not set $UID
if [ ! -z "$UID" ] && [ $UID -eq 0 ]; then
if [ "$1" = "cron" ]; then
# Start the OTOBO Daemon.
# The Daemon will exit immediately when SecureMode = 0.
# But this is OK, as Cron will restart it and it will run when SecureMode = 1.
su -c "./bin/otobo.Daemon.pl start" "$OTOBO_USER"
# Note that in the docker image this file will be located in /var/otobo.

################################################################################
# Declare variables
################################################################################

otobo_next="/var/otobo/otobo_next"

################################################################################
# Declare functions
################################################################################

# Run a watchdog over the Daemon via Cron
# run cron in the foreground
exec cron -f
# nothing will be executed beyond that line,
# because exec replaces running process with the new one
function handle_docker_firsttime() {

if [ ! -d $OTOBO_HOME ]; then
# it is required that /opt/otobo is mounted
print_error "the volume $OTOBO_HOME is not mounted" && exit 1
elif [ ! "$(ls -A $OTOBO_HOME)" ]; then
# first the simple case: there is no previous installation
cp -r "$otobo_next"/* $OTOBO_HOME
# Use the docker specific Config.pm.dist file.
cp --no-clobber $OTOBO_HOME/Kernel/Config.pm.docker.dist $OTOBO_HOME/Kernel/Config.pm
else
is_version_greater_than "$otobo_next/RELEASE" "$OTOBO_HOME/RELEASE"
if [ $? -eq 1 ]; then
upgrade_patchlevel_release
fi
fi

# everything else is run as otobo
exec su "$OTOBO_USER" "$0" -- "$@"
# nothing will be executed beyond that line,
# we are done, docker_firstime has been handled
mv $otobo_next/docker_firsttime $otobo_next/docker_firsttime_handled

# TODO: maybe clean up $otobo_next
}

# An easy way to start bash.
# Or do upgrades.
# Or list files.
function exec_whatever() {
exec "$@"
}

# First try to start the OTOBO Daemon and the continue with cron
function exec_cron() {

# Start the OTOBO Daemon.
# The Daemon will exit immediately when SecureMode = 0.
# But this is OK, as Cron will restart it and it will run when SecureMode = 1.
# Also gracefully handle the case when /opt/otobo is not populated yet.
if [ -f "./bin/otobo.Daemon.pl" ]; then
su -c "./bin/otobo.Daemon.pl start" "$OTOBO_USER"
fi

# Run a watchdog over the Daemon via Cron
# run cron in the foreground
exec cron -f
# nothing will be executed beyond this line,
# because exec replaces running process with the new one
fi
}

# Start the webserver
function exec_web() {

if [ "$1" = "web" ]; then
# maintainance jobs
# TODO: decide whether it makes sense to run these jobs on startup
# TODO: decide whether it makes sense to run these jobs on startup or on upgrade
#perl ./bin/otobo.Console.pl Maint::Config::Rebuild
#perl ./bin/otobo.Console.pl Maint::Cache::Delete

# Start the webserver
#
# For development omit the --env option, thus setting PLACK_ENV to its default value 'development'.
# This enables additional middlewares that are useful durching development.
# For development also enable the -R option.
Expand All @@ -38,10 +76,81 @@ if [ "$1" = "web" ]; then
#
# For debugging reload the complete application for each request
# plackup -L Shotgun --port 5000 bin/psgi-bin/otobo.psgi
plackup --server Gazelle --env deployment --port 5000 bin/psgi-bin/otobo.psgi
exec plackup --server Gazelle --env deployment --port 5000 bin/psgi-bin/otobo.psgi
}

# preserve added files in the previous
function upgrade_patchlevel_release() {
# TODO: maybe backup /opt/otobo, in case somebody did change important files
# for now we only copy files
# Changed files are overwritten, new files are not deleted
cp --recursive $otobo_next/* $OTOBO_HOME
rm $OTOBO_HOME/docker_firsttime
cp --no-clobber $OTOBO_HOME/Kernel/Config.pm.docker.dist $OTOBO_HOME/Kernel/Config.pm

# reinstall package
# TODO: otobo can't get access to the database
$OTOBO_HOME/bin/otobo.Console.pl Admin::Package::ReinstallAll >> /var/otobo/upgrade.log 2>&1
}

print_error() {
echo -e "\e[101m[ERROR]\e[0m $1"
}

else
echo "Unknown option $1. Exiting."
extract_version_from_release_file () {
local release_file="$1"

exit -1
local current_version="$(cat $release_file | grep VERSION | cut -d'=' -f2)"

echo $current_version
}

# gives logical output
# 1 if $1 greater $2
# 0 otherwise
# bash best practice might be the other way araound, but who knows
is_version_greater_than () {
local first_version="$(extract_version_from_release_file $1)"
local second_version="$(extract_version_from_release_file $2)"

# equal is not greater
[[ "$first_version" = "$second_version" ]] && return 0

local greater_version=$(echo -e "$first_version\n$second_version" | sed '/^$/d' | sort --version-sort --reverse | head -1)

[[ "$greater_version" = "$first_version" ]] && return 1

return 0
}

################################################################################
# Do the work
################################################################################

# root handles 'cron' and defers everything else to the OTOBO user
# Also check whether $UID is set, as 'exec su user ...' apparently does not set $UID
if [ ! -z "$UID" ] && [ $UID -eq 0 ]; then
if [ "$1" = "cron" ]; then
exec_cron
fi

# everything else is run as otobo
exec su "$OTOBO_USER" "$0" -- "$@"
# nothing will be executed beyond that line,
# because exec replaces running process with the new one
fi

# now running as $OTOBO_USER

if [ "$1" = "web" ]; then
# first check whether the container is started with a new image
if [ -f "$otobo_next/docker_firsttime" ]; then
handle_docker_firsttime
fi

# start webserver
exec_web
fi

# as a fallback execute the passed command
exec_whatever
31 changes: 23 additions & 8 deletions otobo.web.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ RUN cpanm --with-feature=db:mysql --with-feature=plack --with-feature=devel:dbvi
# --create-home create /opt/otobo
# --shell /bin/bash set the login shell, not used here because otobo is system user
# --comment 'OTOBO user' complete name of the user
ENV OTOBO_USER otobo
ENV OTOBO_USER otobo
ENV OTOBO_GROUP otobo
ENV OTOBO_HOME /opt/otobo
ENV OTOBO_HOME /opt/otobo
RUN useradd --user-group --home-dir $OTOBO_HOME --create-home --shell /bin/bash --comment 'OTOBO user' $OTOBO_USER

# copy the OTOBO installation to /opt/otobo and use it as the working dir
Expand Down Expand Up @@ -65,11 +65,11 @@ RUN perl -p -i.orig -e "s{Host: http://localhost:9200}{Host: http://elastic:9200
# Create ARCHIVE
# Enable bash completion.
# Activate the .dist files.
# Use the docker specific Config.pm.dist file.
RUN mkdir -p var/stats var/packages var/article \
# Config.pm.docker.dist will be copied to Config.pm in entrypoint.sh,
# unless Config.pm already exists
RUN install -d var/stats var/packages var/article \
&& bin/otobo.CheckSum.pl -a create \
&& (echo ". ~/.bash_completion" >> .bash_aliases ) \
&& cp Kernel/Config.pm.docker.dist Kernel/Config.pm \
&& cp Kernel/Config.pod.dist Kernel/Config.pod \
&& cd var/cron && for foo in *.dist; do cp $foo `basename $foo .dist`; done

Expand All @@ -84,14 +84,29 @@ RUN mkdir -p var/tmp \
&& ./bin/Cron.sh start \
&& cp scripts/vim/.vimrc .

# set permissions
# First set permissions.
# At this point /opt/otobo looks fairly sane. But we might have a previous
# version of OTOBO already installed in a volume.
# Therefore move /opt/otobo out of the way.
# But make sure that the empty die /opt/otobo remains and stays writable by $OTOBO_USER.
# Merging current and next version is left to entrypoint.sh.
# TODO: simplify
USER root
RUN perl bin/docker/set_permissions.pl
RUN otobo_next="/var/otobo/otobo_next" \
&& perl bin/docker/set_permissions.pl \
&& install --group $OTOBO_GROUP --owner $OTOBO_USER -d $otobo_next \
&& install --group $OTOBO_GROUP --owner $OTOBO_USER bin/docker/entrypoint.sh /var/otobo \
&& touch /var/otobo/upgrade.log \
&& chown $OTOBO_USER:$OTOBO_GROUP /var/otobo/upgrade.log \
&& chown $OTOBO_USER:$OTOBO_GROUP $OTOBO_HOME \
&& mv $OTOBO_HOME/* $otobo_next \
&& touch $otobo_next/docker_firsttime \
&& chown $OTOBO_USER:$OTOBO_GROUP $otobo_next/docker_firsttime

# start the webserver
# start the OTOBO daemon
# start the Cron watchdog
# Tell the webapplication that it runs in a container.
# The entrypoint takes one command: 'web' or 'cron', web switches to OTOBO_USER
ENV OTOBO_RUNS_UNDER_DOCKER 1
ENTRYPOINT ["bin/docker/entrypoint.sh"]
ENTRYPOINT ["/var/otobo/entrypoint.sh"]

0 comments on commit 620a61f

Please sign in to comment.