Merge pull request #46 from YunoHost/improve-install-script

Clean / improve install script
This commit is contained in:
Alexandre Aubin 2018-03-08 16:56:50 +01:00 committed by GitHub
commit 882ee11397
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# Copyright (C) 2015 kload, beudbeud # Copyright (C) 2015-2017 YunoHost
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -15,81 +15,163 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
YUNOHOST_LOG="/var/log/yunohost-installation.log" set -u
THIS_MACHINE_RELEASE="$(lsb_release -c | awk '{print $2}')"
print() { # Globals
printf "%s\n" "$*";
readonly YUNOHOST_LOG="/var/log/yunohost-installation_$(date +%Y%m%d_%H%M%S).log"
###############################################################################
# Main functions #
###############################################################################
function usage() {
echo "
Usage :
`basename $0` [-a] [-d <DISTRIB>] [-h]
Options :
-a Enable automatic mode. No questions are asked.
This does not perform the post-install step.
-d Choose the distribution to install ('stable', 'testing', 'unstable').
Defaults to 'stable'
-f Ignore checks before starting the installation. Use only if you know
what you are doing.
-h Prints this help and exit
"
} }
notify_about_install_logs() { function parse_options()
print " {
Installation logs are located in $YUNOHOST_LOG AUTOMODE=0
" 1>&2 DISTRIB=stable
BUILD_IMAGE=0
FORCE=0
while getopts ":aid:fh" option; do
case $option in
a)
AUTOMODE=1
export DEBIAN_FRONTEND=noninteractive
;;
d)
DISTRIB=$OPTARG
;;
f)
FORCE=1
;;
i)
# This hidden option will allow to build generic image for Rpi/Olimex
BUILD_IMAGE=1
;;
h)
usage
exit 0
;;
:)
usage
exit 1
;;
\?)
usage
exit 1
;;
esac
done
} }
success() { function main()
tput setf 2 {
print "Success !" parse_options "$@"
tput sgr 0
notify_about_install_logs check_assertions
step upgrade_system || die "Unable to update the system"
step install_script_dependencies || die "Unable to install dependencies to install script"
step create_custom_config || die "Creating custom configuration file /etc/yunohost/yunohost.conf failed"
step confirm_installation || die "Installation cancelled at your request"
step setup_package_source || die "Setting up deb package sources failed"
step apt_update || die "Error caught during 'apt-get update'"
step register_debconf || die "Unable to insert new values into debconf database"
step workaround_avahi_installation || die "Unable to install workaround for avahi installation"
step install_yunohost_packages || die "Installation of Yunohost packages failed"
step restart_services || die "Error caught during services restart"
if is_raspbian ; then
step del_user_pi || die "Unable to delete user pi"
step setup_firstboot || die "Unable to setup firstboot"
fi
if [[ "$BUILD_IMAGE" == "1" ]] ; then
step clean_image || die "Unable to clean image"
fi
if is_raspbian ; then
# Reboot should be done before postinstall to be able to run iptables rules
reboot
fi
step post_install || die "Post-installation failed"
info "Installation logs are available in $YUNOHOST_LOG"
success "YunoHost installation completed !"
exit 0
} }
die() { ###############################################################################
# Print to log file # Helpers #
print " ###############################################################################
Failure !
The following error was caught during Yunohost installation :
$1 readonly normal=$(printf '\033[0m')
" >> $YUNOHOST_LOG readonly bold=$(printf '\033[1m')
readonly faint=$(printf '\033[2m')
readonly underline=$(printf '\033[4m')
readonly negative=$(printf '\033[7m')
readonly red=$(printf '\033[31m')
readonly green=$(printf '\033[32m')
readonly orange=$(printf '\033[33m')
readonly blue=$(printf '\033[34m')
readonly yellow=$(printf '\033[93m')
readonly white=$(printf '\033[39m')
# Print to terminal function success()
tput setf 4 {
print "Failure !" local msg=${1}
tput sgr 0 echo "[${bold}${green} OK ${normal}] ${msg}" | tee -a $YUNOHOST_LOG
print "\
The following error was caught during YunoHost installation :
$1
" 1>&2
notify_about_install_logs
exit "${2:-1}"
} }
step() { function info()
printf "[ $(date --rfc-3339=seconds) ] ----- [ entering %-30s ]\n" "$1" >> $YUNOHOST_LOG {
local msg=${1}
echo "[${bold}${blue}INFO${normal}] ${msg}" | tee -a $YUNOHOST_LOG
}
function warn()
{
local msg=${1}
echo "[${bold}${orange}WARN${normal}] ${msg}" | tee -a $YUNOHOST_LOG >&2
}
function error()
{
local msg=${1}
echo "[${bold}${red}FAIL${normal}] ${msg}" | tee -a $YUNOHOST_LOG >&2
}
function die() {
error "$1"
info "Installation logs are available in $YUNOHOST_LOG"
exit 1
}
function step() {
info "Running $1"
$* $*
local return_code="$?" local return_code="$?"
return $return_code return $return_code
} }
ensure_root() { function apt_get_wrapper() {
if [[ "$(id -u)" != "0" ]] ;
then
return 1
fi
return 0
}
ensure_pi_logout() {
who | grep pi > /dev/null && return 1
return 0
}
is_raspbian() {
# On Raspbian image lsb_release is available
if [[ "$(lsb_release -i -s 2> /dev/null)" != "Raspbian" ]] ;
then
return 1
fi
return 0
}
apt_get_wrapper() {
if [[ "$AUTOMODE" == "0" ]] ; if [[ "$AUTOMODE" == "0" ]] ;
then then
debconf-apt-progress \ debconf-apt-progress \
@ -101,26 +183,68 @@ apt_get_wrapper() {
fi fi
} }
upgrade_system() {
function apt_update() {
apt_get_wrapper update
}
###############################################################################
# Installation steps #
###############################################################################
function check_assertions()
{
# Assert we're on Debian
# Note : we do not rely on lsb_release to avoid installing a dependency
# only to check this...
[[ -f "/etc/debian_version" ]] || die "This script can only be ran on Debian."
# Assert we're on Stretch
# Note : we do not rely on lsb_release to avoid installing a dependency
# only to check this...
[[ "$(cat /etc/debian_version)" =~ ^9.* ]] || die "This script can only be ran on Debian Stretch."
# Assert we're root
[[ "$(id -u)" == "0" ]] || die "This script must be run as root."
# Assert systemd is installed
command -v systemctl > /dev/null || die "YunoHost requires systemd to be installed."
# If we're on Raspbian, we want the user 'pi' to be logged out because
# it's going to be deleted for security reasons...
if is_raspbian ; then
user_pi_logged_out || die "The user pi should be logged out."
fi
# Check possible conflict with apache, bind9.
[[ -z "$(dpkg --get-selections | grep -v deinstall | grep 'bind9 ')" ]] || [[ "$FORCE" == "1" ]] \
|| die "Bind9 is installed and might conflict with dnsmasq. Uninstall it first, or if you know what you are doing, run this script with -f."
[[ -z "$(dpkg --get-selections | grep -v deinstall | grep 'apache2 ')" ]] || [[ "$FORCE" == "1" ]] \
|| die "Apache is installed and might conflict with nginx. Uninstall it first, or if you know what you are doing, run this script with -f."
}
function upgrade_system() {
apt_get_wrapper update \ apt_get_wrapper update \
|| return 1 || return 1
apt_get_wrapper -y dist-upgrade \ apt_get_wrapper -y dist-upgrade \
|| return 2 || return 2
if is_raspbian ; then if is_raspbian ; then
apt_get_wrapper -o Dpkg::Options::="--force-confold" \ apt_get_wrapper -o Dpkg::Options::="--force-confold" \
-y --force-yes install rpi-update \ -y --force-yes install rpi-update \
|| return 3 || return 3
rpi-update >> $YUNOHOST_LOG 2>&1 \ rpi-update >> $YUNOHOST_LOG 2>&1 \
|| return 4 || return 4
fi fi
} }
installscript_dependencies() { function install_script_dependencies() {
# dependencies of the install script itself # dependencies of the install script itself
local DEPENDENCIES="lsb-release wget whiptail" local DEPENDENCIES="lsb-release wget whiptail"
@ -133,30 +257,9 @@ installscript_dependencies() {
-y --force-yes install \ -y --force-yes install \
$DEPENDENCIES \ $DEPENDENCIES \
|| return 1 || return 1
#
# Temporary comment for stretch...
# To be removed if really not needed
#
#if is_raspbian ; then
# DEPENDENCIES="ssl-cert lua-event lua-expat lua-socket lua-sec lua-filesystem"
# apt_get_wrapper -o Dpkg::Options::="--force-confold" \
# -y --force-yes install \
# $DEPENDENCIES \
# || return 1
# wget -q https://build.yunohost.org/metronome_3.7.9+33b7572-1_armhf.deb \
# || return 1
# sha256sum -c <<<"d19c6b08afb8674d1257dc3349a60e88218c4c01133c53c1fdcb02e86b415a40 metronome_3.7.9+33b7572-1_armhf.deb" \
# || return 1
# dpkg -i metronome_3.7.9+33b7572-1_armhf.deb >> $YUNOHOST_LOG 2>&1 \
# || return 1
# apt-mark hold metronome >> $YUNOHOST_LOG 2>&1 \
# || return 1
#fi
} }
create_custom_config() { function create_custom_config() {
# Create YunoHost configuration folder # Create YunoHost configuration folder
mkdir -p /etc/yunohost/ mkdir -p /etc/yunohost/
@ -164,7 +267,7 @@ create_custom_config() {
touch /etc/yunohost/from_script touch /etc/yunohost/from_script
} }
confirm_installation() { function confirm_installation() {
[[ "$AUTOMODE" == "1" ]] && return 0 [[ "$AUTOMODE" == "1" ]] && return 0
local text=" local text="
@ -183,19 +286,10 @@ Are you sure you want to proceed with the installation of Yunohost?
whiptail --title "Yunohost Installation" --yesno "$text" 20 78 whiptail --title "Yunohost Installation" --yesno "$text" 20 78
} }
setup_package_source() { function setup_package_source() {
local CUSTOMAPT=/etc/apt/sources.list.d/yunohost.list local CUSTOMAPT=/etc/apt/sources.list.d/yunohost.list
# Check current system version and dependencies
if [[ ! $THIS_MACHINE_RELEASE =~ ^jessie|stretch$ ]]; then
echo "Current $DISTRIB only works on Debian Jessie or Stretch for the moment."
return 1
elif ! command -v systemctl > /dev/null ; then
echo "Current $DISTRIB only works with systemd for the moment."
return 1
fi
# Debian repository # Debian repository
# FIXME : move "vinaigrette." to "repo." at some point... # FIXME : move "vinaigrette." to "repo." at some point...
@ -214,12 +308,8 @@ setup_package_source() {
wget -O- https://vinaigrette.yunohost.org/yunohost.asc -q | apt-key add -qq - >/dev/null 2>&1 wget -O- https://vinaigrette.yunohost.org/yunohost.asc -q | apt-key add -qq - >/dev/null 2>&1
} }
apt_update() { function register_debconf() {
apt_get_wrapper update debconf-set-selections << EOF
}
register_debconf() {
debconf-set-selections << EOF
slapd slapd/password1 password yunohost slapd slapd/password1 password yunohost
slapd slapd/password2 password yunohost slapd slapd/password2 password yunohost
slapd slapd/domain string yunohost.org slapd slapd/domain string yunohost.org
@ -243,7 +333,7 @@ libnss-ldapd libnss-ldapd/nsswitch multiselect group, passwd, shadow
EOF EOF
} }
workaround_avahi_installation() { function workaround_avahi_installation() {
# When attempting several installation of Yunohost on the same host # When attempting several installation of Yunohost on the same host
# with a light VM system like LXC # with a light VM system like LXC
@ -262,7 +352,7 @@ workaround_avahi_installation() {
# Return without error if avahi already exists # Return without error if avahi already exists
if id avahi > /dev/null 2>&1 ; then if id avahi > /dev/null 2>&1 ; then
print "User avahi already exists (with uid $(id avahi)), skipping avahi workaround" >> $YUNOHOST_LOG info "User avahi already exists (with uid $(id avahi)), skipping avahi workaround"
return 0 return 0
fi fi
@ -273,7 +363,7 @@ workaround_avahi_installation() {
avahi_id=$((500 + RANDOM % 500)) avahi_id=$((500 + RANDOM % 500))
done done
print "Workaround for avahi : creating avahi user with uid $avahi_id" >> $YUNOHOST_LOG info "Workaround for avahi : creating avahi user with uid $avahi_id"
# Use the same adduser parameter as in the avahi-daemon postinst script # Use the same adduser parameter as in the avahi-daemon postinst script
# Just specify --uid explicitely # Just specify --uid explicitely
@ -283,11 +373,11 @@ workaround_avahi_installation() {
--uid $avahi_id --uid $avahi_id
} }
install_yunohost_packages() { function install_yunohost_packages() {
# Allow sudo removal even if no root password has been set (on some DO # Allow sudo removal even if no root password has been set (on some DO
# droplet or Vagrant virtual machines), as YunoHost use sudo-ldap # droplet or Vagrant virtual machines), as YunoHost use sudo-ldap
export SUDO_FORCE_REMOVE=yes export SUDO_FORCE_REMOVE=yes
# On some machines (e.g. OVH VPS), the /etc/resolv.conf is immutable # On some machines (e.g. OVH VPS), the /etc/resolv.conf is immutable
# We need to make it mutable for the resolvconf dependency to be installed # We need to make it mutable for the resolvconf dependency to be installed
chattr -i /etc/resolv.conf 2>/dev/null || true chattr -i /etc/resolv.conf 2>/dev/null || true
@ -299,7 +389,7 @@ install_yunohost_packages() {
yunohost yunohost-admin postfix yunohost yunohost-admin postfix
} }
restart_services() { function restart_services() {
service slapd restart service slapd restart
# service yunohost-firewall start # service yunohost-firewall start
service unscd restart service unscd restart
@ -309,57 +399,7 @@ restart_services() {
return 0 return 0
} }
del_user_pi() { function post_install() {
deluser --remove-all-files pi >> $YUNOHOST_LOG 2>&1
}
change_hostname() {
sed -i 's/raspberrypi/yunohost/g' /etc/hosts
sed -i 's/raspberrypi/yunohost/g' /etc/hostname
}
setup_firstboot() {
cat > /etc/init.d/yunohost-firstboot << EOF
#!/bin/sh
### BEGIN INIT INFO
# Provides: expand rootfs and Generates new ssh host keys on first boot
# Required-Start: \$remote_fs \$syslog
# Required-Stop: \$remote_fs \$syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Generates new ssh host keys on first boot
# Description: Generates apt-get --purge clean new ssh host keys on $
### END INIT INFO
echo "Expanding rootfs ..."
raspi-config --expand-rootfs
echo "Removing myself ..."
insserv -r /etc/init.d/yunohost-firstboot
rm -f /etc/init.d/yunohost-firstboot
rm /etc/yunohost/firstboot
echo "Rebooting ..."
reboot
EOF
chmod a+x /etc/init.d/yunohost-firstboot
insserv /etc/init.d/yunohost-firstboot
touch /etc/yunohost/firstboot
}
clean_image() {
# Delete SSH keys
rm -f /etc/ssh/ssh_host_* >> $YUNOHOST_LOG 2>&1
yes | ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa >> $YUNOHOST_LOG 2>&1
yes | ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N '' -t dsa >> $YUNOHOST_LOG 2>&1
yes | ssh-keygen -f /etc/ssh/ssh_host_ecdsa_key -N '' -t ecdsa -b 521 >> $YUNOHOST_LOG 2>&1
# Deleting logs ...
find /var/log -type f -exec rm {} \; >> $YUNOHOST_LOG 2>&1
# Purging apt ...
apt-get --purge clean >> $YUNOHOST_LOG 2>&1
}
post_install() {
# No postinstall in auto mode # No postinstall in auto mode
[[ "$AUTOMODE" == "1" ]] && return 0 [[ "$AUTOMODE" == "1" ]] && return 0
@ -406,140 +446,74 @@ Do you want to try again now?
return 0 return 0
} }
usage() { ###############################################################################
print " # Raspbian specific stuff #
Usage : ###############################################################################
`basename $0` [-a] [-d <DISTRIB>] [-h]
Options : function is_raspbian() {
-a Enable automatic mode. No questions are asked. # On Raspbian image lsb_release is available
This does not perform the post-install step. if [[ "$(lsb_release -i -s 2> /dev/null)" != "Raspbian" ]] ;
-d Choose the distribution to install ('stable', 'testing', 'unstable'). then
Defaults to 'stable' return 1
-h Prints this help and exit fi
" return 0
} }
# Treat unset variables as an error when performing function user_pi_logged_out() {
# parameter expansion. An error message will be written who | grep pi > /dev/null && return 1
# to the standard error, and a non-interactive shell will exit. return 0
set -u }
AUTOMODE=0 function del_user_pi() {
DISTRIB=stable deluser --remove-all-files pi >> $YUNOHOST_LOG 2>&1
BUILD_IMAGE=0 }
while getopts ":aid:h" option; do
case $option in
a)
AUTOMODE=1
export DEBIAN_FRONTEND=noninteractive
;;
d)
DISTRIB=$OPTARG
;;
i)
# This hidden option will allow to build generic image for Rpi/Olimex
BUILD_IMAGE=1
;;
h)
usage
exit 0
;;
:)
usage
exit 1
;;
\?)
usage
exit 1
;;
esac
done
if ! step ensure_root ; then function setup_firstboot() {
die "This script must be run as root" 1
fi
if is_raspbian ; then cat > /etc/init.d/yunohost-firstboot << EOF
if ! step ensure_pi_logout ; then #!/bin/sh
die "The user pi should be logged out" 14 ### BEGIN INIT INFO
fi # Provides: expand rootfs and Generates new ssh host keys on first boot
fi # Required-Start: \$remote_fs \$syslog
# Required-Stop: \$remote_fs \$syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Generates new ssh host keys on first boot
# Description: Generates apt-get --purge clean new ssh host keys on $
### END INIT INFO
echo "Expanding rootfs ..."
raspi-config --expand-rootfs
echo "Removing myself ..."
insserv -r /etc/init.d/yunohost-firstboot
rm -f /etc/init.d/yunohost-firstboot
rm /etc/yunohost/firstboot
echo "Rebooting ..."
reboot
EOF
chmod a+x /etc/init.d/yunohost-firstboot
insserv /etc/init.d/yunohost-firstboot
touch /etc/yunohost/firstboot
}
if ! step upgrade_system ; then ###############################################################################
die "Unable to update the system" 2 # Image building specific stuff #
fi ###############################################################################
if ! step installscript_dependencies ; then function clean_image() {
die "Unable to install dependencies to install script" 3 # Delete SSH keys
fi rm -f /etc/ssh/ssh_host_* >> $YUNOHOST_LOG 2>&1
yes | ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa >> $YUNOHOST_LOG 2>&1
yes | ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N '' -t dsa >> $YUNOHOST_LOG 2>&1
yes | ssh-keygen -f /etc/ssh/ssh_host_ecdsa_key -N '' -t ecdsa -b 521 >> $YUNOHOST_LOG 2>&1
if ! step create_custom_config ; then # Deleting logs ...
die "Creating custom configuration file /etc/yunohost/yunohost.conf failed" 4 find /var/log -type f -exec rm {} \; >> $YUNOHOST_LOG 2>&1
fi
# if ! step set_domain ; then # Purging apt ...
# die "Setting hostname failed" 5 apt-get --purge clean >> $YUNOHOST_LOG 2>&1
# fi }
if ! step confirm_installation ; then
die "Installation cancelled at your request" 6
fi
if ! step setup_package_source ; then
die "Setting up deb package sources failed" 7
fi
if ! step apt_update ; then
die "Error caught during 'apt-get update'" 8
fi
if ! step register_debconf ; then
die "Unable to insert new values into debconf database" 9
fi
if ! step workaround_avahi_installation ; then
die "Unable to install workaround for avahi installation" 10
fi
if ! step install_yunohost_packages ; then
die "Installation of Yunohost packages failed" 11
fi
if ! step restart_services ; then
die "Error caught during services restart" 12
fi
if is_raspbian ; then
if ! step del_user_pi ; then
die "Unable to delete user pi" 15
fi
if ! step change_hostname ; then
die "Unable to change hostname" 16
fi
if ! step setup_firstboot ; then
die "Unable to setup firstboot" 17
fi
fi
if [[ "$BUILD_IMAGE" == "1" ]] ; then
if ! step clean_image ; then
die "Unable to clean image" 18
fi
fi
if is_raspbian ; then
# Reboot should be done before postinstall to be able to run iptables rules
reboot
fi
if ! step post_install ; then
die "Post-installation failed" 13
fi
# Success ! ###############################################################################
success
exit 0 main "$@"