# #!/bin/bash #================================================= # Globals variables #================================================= # -q aims to disable the display of 'Debian GNU/Linux' each time a command is ran arg_ssh="-tt -q" current_snapshot=snap0 #================================================= # RUNNING SNAPSHOT #================================================= CREATE_LXC_SNAPSHOT () { # Create a temporary snapshot # snap1 for subpath or snap2 for root install snap_number=$1 start_timer # Check all the witness files, to verify if them still here check_witness_files >&2 # Stop the container, before its snapshot sudo lxc-stop --name $LXC_NAME >&2 # Remove swap files to avoid killing the CI with huge snapshots. local swap_file="$LXC_ROOTFS/swap_$app_id" if sudo test -e "$swap_file" then sudo swapoff "$swap_file" sudo rm "$swap_file" fi # Check if the snapshot already exist if [ ! -e "$LXC_SNAPSHOTS/snap$snap_number" ] then log_debug "snap$snap_number doesn't exist, its first creation can takes a little while." >&2 # Create the snapshot. sudo lxc-snapshot --name $LXC_NAME >> "$complete_log" 2>&1 # lxc always creates the first snapshot it can creates. # So if snap1 doesn't exist and you try to create snap2, it will be named snap1. if [ "$snap_number" == "2" ] && [ ! -e "$LXC_SNAPSHOTS/snap2" ] then # Rename snap1 to snap2 sudo mv "$LXC_SNAPSHOTS/snap1" "$LXC_SNAPSHOTS/snap2" fi fi # Update the snapshot with rsync to clone the current lxc state sudo rsync --acls --archive --delete --executability --itemize-changes --xattrs "$LXC_ROOTFS/" "$LXC_SNAPSHOTS/snap$snap_number/rootfs/" > /dev/null 2>> "$complete_log" # Set this snapshot as the current snapshot current_snapshot=snap$snap_number stop_timer 1 # Restart the container, after the snapshot LXC_START "true" >&2 } LOAD_LXC_SNAPSHOT () { # Use a temporary snapshot, if it already exists # $1 = Name of the snapshot to use current_snapshot=$1 start_timer # Fix the missing hostname in the hosts file... echo "127.0.0.1 $LXC_NAME" | sudo tee --append "$LXC_SNAPSHOTS/$current_snapshot/rootfs/etc/hosts" > /dev/null # Restore this snapshot. sudo rsync --acls --archive --delete --executability --itemize-changes --xattrs "$LXC_SNAPSHOTS/$current_snapshot/rootfs/" "$LXC_ROOTFS/" > /dev/null 2>> "$complete_log" stop_timer 1 # Fake the yunohost_result return code of the installation yunohost_result=0 } #================================================= is_lxc_running () { sudo lxc-info --name=$LXC_NAME | grep --quiet "RUNNING" } LXC_INIT () { # Clean previous remaining swap files sudo swapoff $LXC_ROOTFS/swap_* 2>/dev/null sudo rm --force $LXC_ROOTFS/swap_* sudo swapoff $LXC_SNAPSHOTS/snap0/rootfs/swap_* 2>/dev/null sudo rm --force $LXC_SNAPSHOTS/snap0/rootfs/swap_* sudo swapoff $LXC_SNAPSHOTS/snap1/rootfs/swap_* 2>/dev/null sudo rm --force $LXC_SNAPSHOTS/snap1/rootfs/swap_* sudo swapoff $LXC_SNAPSHOTS/snap2/rootfs/swap_* 2>/dev/null sudo rm --force $LXC_SNAPSHOTS/snap2/rootfs/swap_* # Initialize LXC network # Activate the bridge echo "Initialize network for LXC." sudo ifup $LXC_BRIDGE --interfaces=/etc/network/interfaces.d/$LXC_BRIDGE | tee --append "$complete_log" 2>&1 # Activate iptables rules echo "Activate iptables rules." sudo iptables --append FORWARD --in-interface $LXC_BRIDGE --out-interface $MAIN_NETWORK_INTERFACE --jump ACCEPT | tee --append "$complete_log" 2>&1 sudo iptables --append FORWARD --in-interface $MAIN_NETWORK_INTERFACE --out-interface $LXC_BRIDGE --jump ACCEPT | tee --append "$complete_log" 2>&1 sudo iptables --table nat --append POSTROUTING --source $LXC_NETWORK.0/24 --jump MASQUERADE | tee --append "$complete_log" 2>&1 } LXC_START () { # Start the lxc container and execute the given command in it # $1 = Command to execute in the container start_timer # Try to start the container 3 times. local max_try=3 local i=0 while [ $i -lt $max_try ] do i=$(( $i +1 )) # Start the container and log the booting process in ./lxc_boot.log # Try to start only if the container is not already started if ! is_lxc_running; then log_debug "Start the LXC container" >> "$complete_log" sudo lxc-start --name=$LXC_NAME --daemon --logfile "./lxc_boot.log" | tee --append "$complete_log" 2>&1 local avoid_witness=0 else log_debug "A LXC container is already running" local avoid_witness=1 fi # Try to connect 5 times local j=0 for j in `seq 1 5` do log_debug "." >> "$complete_log" # Try to connect with ssh to check if the container is ready to work. if ssh $arg_ssh -o ConnectTimeout=10 $LXC_NAME "exit 0" > /dev/null 2>&1; then # Break the for loop if the container is ready. break fi sleep 1 done echo "" if [ "$(uname -m)" == "aarch64" ] then sleep 30 fi local failstart=0 # Check if the container is running if ! is_lxc_running; then log_critical "The LXC container didn't start..." failstart=1 if [ $i -ne $max_try ]; then log_info "Rebooting the container..." fi LXC_STOP # Try to ping security.debian.org to check the connectivity from the container elif ! ssh $arg_ssh -o ConnectTimeout=60 $LXC_NAME "sudo ping -q -c 2 security.debian.org > /dev/null 2>&1; exit \$?" >> "$complete_log" 2>&1 then log_critical "The container failed to connect to internet..." failstart=1 if [ $i -ne $max_try ]; then log_info "Rebooting the container..." fi LXC_STOP # Create files to check if the remove script does not remove them accidentally else [ $avoid_witness -eq 0 ] && set_witness_files # Break the for loop if the container is ready. break fi # Fail if the container failed to start if [ $i -eq $max_try ] && [ $failstart -eq 1 ] then send_email () { # Send an email only if it's a CI environment if [ $type_exec_env -ne 0 ] then ci_path=$(grep "CI_URL=" "./../config" | cut -d= -f2) local subject="[YunoHost] Container in trouble on $ci_path." local message="The container failed to start $max_try times on $ci_path. $lxc_check_result Please have a look to the log of lxc_check: $(cat "./lxc_check.log")" if [ $lxc_check -eq 2 ]; then # Add the log of lxc_build message="$message Here the log of lxc_build: $(cat "./sub_scripts/Build_lxc.log")" fi dest=$(grep 'dest=' "./../config" | cut -d= -f2) mail -s "$subject" "$dest" <<< "$message" fi } log_critical "The container failed to start $max_try times..." log_info "Boot log:\n" cat "./lxc_boot.log" | tee --append "$complete_log" log_info "lxc_check will try to fix the container..." ./sub_scripts/lxc_check.sh --no-lock | tee "./lxc_check.log" # PIPESTATUS is an array with the exit code of each command followed by a pipe local lxc_check=${PIPESTATUS[0]} LXC_INIT if [ $lxc_check -eq 0 ]; then local lxc_check_result="The container seems to be ok, according to lxc_check." log_success "$lxc_check_result" send_email i=0 elif [ $lxc_check -eq 1 ]; then local lxc_check_result="An error has happened with the host. Please check the configuration." log_critical "$lxc_check_result" send_email stop_timer 1 return 1 elif [ $lxc_check -eq 2 ]; then local lxc_check_result="The container is broken, it will be rebuilt." log_critical "$lxc_check_result" ./sub_scripts/lxc_build.sh LXC_INIT send_email i=0 elif [ $lxc_check -eq 3 ]; then local lxc_check_result="The container has been fixed by lxc_check." log_success "$lxc_check_result" send_email i=0 fi fi done stop_timer 1 start_timer # Copy the package into the container. rsync -rq --delete "$package_path" "$LXC_NAME": >> "$complete_log" 2>&1 # Execute the command given in argument in the container and log its results. ssh $arg_ssh $LXC_NAME "$1; exit $?" | tee -a "$complete_log" # Store the return code of the command local returncode=${PIPESTATUS[0]} stop_timer 1 # Return the exit code of the ssh command return $returncode } LXC_STOP () { # Stop and restore the LXC container start_timer # Stop the LXC container if is_lxc_running; then log_debug "Stop the LXC container" sudo lxc-stop --name=$LXC_NAME | tee --append "$complete_log" 2>&1 fi # Fix the missing hostname in the hosts file # If the hostname is missing in /etc/hosts inside the snapshot if ! sudo grep --quiet "$LXC_NAME" "$LXC_SNAPSHOTS/$current_snapshot/rootfs/etc/hosts" then # If the hostname was replaced by name of the snapshot, fix it if sudo grep --quiet "$current_snapshot" "$LXC_SNAPSHOTS/$current_snapshot/rootfs/etc/hosts" then # Replace snapX by the real hostname sudo sed --in-place "s/$current_snapshot/$LXC_NAME/" "$LXC_SNAPSHOTS/$current_snapshot/rootfs/etc/hosts" else # Otherwise, simply add the hostname echo "127.0.0.1 $LXC_NAME" | sudo tee --append "$LXC_SNAPSHOTS/$current_snapshot/rootfs/etc/hosts" > /dev/null fi fi # Restore the snapshot. log_debug "Restore the previous snapshot." sudo rsync --acls --archive --delete --executability --itemize-changes --xattrs "$LXC_SNAPSHOTS/$current_snapshot/rootfs/" "$LXC_ROOTFS/" > /dev/null 2>> "$complete_log" stop_timer 1 } LXC_TURNOFF () { # Disable LXC network log_debug "Disable iptables rules." if sudo iptables --check FORWARD --in-interface $LXC_BRIDGE --out-interface $MAIN_NETWORK_INTERFACE --jump ACCEPT 2> /dev/null then sudo iptables --delete FORWARD --in-interface $LXC_BRIDGE --out-interface $MAIN_NETWORK_INTERFACE --jump ACCEPT >> "$complete_log" 2>&1 fi if sudo iptables --check FORWARD --in-interface $MAIN_NETWORK_INTERFACE --out-interface $LXC_BRIDGE --jump ACCEPT 2> /dev/null then sudo iptables --delete FORWARD --in-interface $MAIN_NETWORK_INTERFACE --out-interface $LXC_BRIDGE --jump ACCEPT | tee --append "$complete_log" 2>&1 fi if sudo iptables --table nat --check POSTROUTING --source $LXC_NETWORK.0/24 --jump MASQUERADE 2> /dev/null then sudo iptables --table nat --delete POSTROUTING --source $LXC_NETWORK.0/24 --jump MASQUERADE | tee --append "$complete_log" 2>&1 fi log_debug "Disable the network bridge." if sudo ifquery $LXC_BRIDGE --state > /dev/null then sudo ifdown --force $LXC_BRIDGE | tee --append "$complete_log" 2>&1 fi # Set snap0 as the current snapshot current_snapshot=snap0 }