diff --git a/lib/lxc.sh b/lib/lxc.sh deleted file mode 100644 index f52ab43..0000000 --- a/lib/lxc.sh +++ /dev/null @@ -1,223 +0,0 @@ -#!/bin/bash - -#================================================= -# RUNNING SNAPSHOT -#================================================= - -LXC_CREATE () { - log_info "Launching new LXC $LXC_NAME ..." - # Check if we can launch container from YunoHost remote image - if lxc remote list | grep -q "yunohost" && lxc image list yunohost:$LXC_BASE | grep -q -w $LXC_BASE; then - lxc launch yunohost:$LXC_BASE $LXC_NAME \ - -c security.nesting=true \ - -c security.privileged=true \ - -c limits.memory=80% \ - -c limits.cpu.allowance=80% \ - >>/proc/self/fd/3 - # Check if we can launch container from a local image - elif lxc image list $LXC_BASE | grep -q -w $LXC_BASE; then - lxc launch $LXC_BASE $LXC_NAME \ - -c security.nesting=true \ - -c security.privileged=true \ - -c limits.memory=80% \ - -c limits.cpu.allowance=80% \ - >>/proc/self/fd/3 - else - log_critical "Can't find base image $LXC_BASE, run ./package_check.sh --rebuild" - fi - - pipestatus="${PIPESTATUS[0]}" - location=$(lxc list --format json | jq -e --arg LXC_NAME $LXC_NAME '.[] | select(.name==$LXC_NAME) | .location' | tr -d '"') - [[ "$location" != "none" ]] && log_info "... on $location" - - [[ "$pipestatus" -eq 0 ]] || exit 1 - - _LXC_START_AND_WAIT $LXC_NAME - set_witness_files - lxc snapshot $LXC_NAME snap0 -} - -LXC_SNAPSHOT_EXISTS() { - local snapname=$1 - lxc list --format json \ - | jq -e --arg LXC_NAME $LXC_NAME --arg snapname $snapname \ - '.[] | select(.name==$LXC_NAME) | .snapshots[] | select(.name==$snapname)' \ - >/dev/null -} - -CREATE_LXC_SNAPSHOT () { - # Create a temporary snapshot - - local snapname=$1 - - start_timer - - # Check all the witness files, to verify if them still here - check_witness_files >&2 - - # Remove swap files to avoid killing the CI with huge snapshots. - CLEAN_SWAPFILES - - LXC_STOP $LXC_NAME - - # Check if the snapshot already exist - if ! LXC_SNAPSHOT_EXISTS "$snapname" - then - log_info "(Creating snapshot $snapname ...)" - lxc snapshot $LXC_NAME $snapname - fi - - _LXC_START_AND_WAIT $LXC_NAME - - stop_timer 1 -} - -LOAD_LXC_SNAPSHOT () { - local snapname=$1 - log_debug "Loading snapshot $snapname ..." - - # Remove swap files before restoring the snapshot. - CLEAN_SWAPFILES - - LXC_STOP $LXC_NAME - - lxc restore $LXC_NAME $snapname - lxc start $LXC_NAME - _LXC_START_AND_WAIT $LXC_NAME -} - -#================================================= - -LXC_EXEC () { - # Start the lxc container and execute the given command in it - local cmd=$1 - - _LXC_START_AND_WAIT $LXC_NAME - - start_timer - - # Execute the command given in argument in the container and log its results. - lxc exec $LXC_NAME --env PACKAGE_CHECK_EXEC=1 -t -- /bin/bash -c "$cmd" | tee -a "$complete_log" $current_test_log - - # Store the return code of the command - local returncode=${PIPESTATUS[0]} - - log_debug "Return code: $returncode" - - stop_timer 1 - # Return the exit code of the ssh command - return $returncode -} - -LXC_STOP () { - local container_to_stop=$1 - # (We also use timeout 30 in front of the command because sometime lxc - # commands can hang forever despite the --timeout >_>...) - timeout 30 lxc stop --timeout 15 $container_to_stop 2>/dev/null - - # If the command times out, then add the option --force - if [ $? -eq 124 ]; then - timeout 30 lxc stop --timeout 15 $container_to_stop --force 2>/dev/null - fi - -} - -LXC_RESET () { - # If the container exists - if lxc info $LXC_NAME >/dev/null 2>/dev/null; then - # Remove swap files before deletting the continer - CLEAN_SWAPFILES - fi - - LXC_STOP $LXC_NAME - - if lxc info $LXC_NAME >/dev/null 2>/dev/null; then - local current_storage=$(lxc list $LXC_NAME --format json --columns b | jq '.[].expanded_devices.root.pool') - swapoff "$(lxc storage get $current_storage source)/containers/$LXC_NAME/rootfs/swap" 2>/dev/null - fi - - lxc delete $LXC_NAME --force 2>/dev/null -} - - -_LXC_START_AND_WAIT() { - - restart_container() - { - LXC_STOP $1 - lxc start "$1" - } - - # Try to start the container 3 times. - local max_try=3 - local i=0 - while [ $i -lt $max_try ] - do - i=$(( i +1 )) - local failstart=0 - - # Wait for container to start, we are using systemd to check this, - # for the sake of brevity. - for j in $(seq 1 10); do - if lxc exec "$1" -- systemctl isolate multi-user.target >/dev/null 2>/dev/null; then - break - fi - - if [ "$j" == "10" ]; then - log_debug 'Failed to start the container ... restarting ...' - failstart=1 - - restart_container "$1" - fi - - sleep 1s - done - - # Wait for container to access the internet - for j in $(seq 1 10); do - if lxc exec "$1" -- curl -s http://wikipedia.org > /dev/null 2>/dev/null; then - break - fi - - if [ "$j" == "10" ]; then - log_debug 'Failed to access the internet ... restarting' - failstart=1 - - restart_container "$1" - fi - - sleep 1s - done - - # Has started and has access to the internet - if [ $failstart -eq 0 ] - then - break - fi - - # Fail if the container failed to start - if [ $i -eq $max_try ] && [ $failstart -eq 1 ] - then - log_error "The container miserably failed to start or to connect to the internet" - lxc info --show-log $1 - return 1 - fi - done - - LXC_IP=$(lxc exec $1 -- hostname -I | cut -d' ' -f1 | grep -E -o "\<[0-9.]{8,}\>") -} - -CLEAN_SWAPFILES() { - # Restart it if needed - if [ "$(lxc info $LXC_NAME | grep Status | awk '{print tolower($2)}')" != "running" ]; then - lxc start $LXC_NAME - _LXC_START_AND_WAIT $LXC_NAME - fi - lxc exec $LXC_NAME -- bash -c 'for swapfile in $(ls /swap_* 2>/dev/null); do swapoff $swapfile; done' - lxc exec $LXC_NAME -- bash -c 'for swapfile in $(ls /swap_* 2>/dev/null); do rm -f $swapfile; done' -} - -RUN_INSIDE_LXC() { - lxc exec $LXC_NAME -- "$@" -} - diff --git a/lib/tests.sh b/lib/tests.sh index 5685fdf..3b9a9e0 100644 --- a/lib/tests.sh +++ b/lib/tests.sh @@ -13,12 +13,12 @@ _RUN_YUNOHOST_CMD() { lxc file push -p -r "$package_path" $LXC_NAME/app_folder --quiet # --output-as none is to disable the json-like output for some commands like backup create - LXC_EXEC "yunohost --output-as none --debug $1" \ + ynh_lxc_pc_exec --name=$LXC_NAME --command="yunohost --output-as none --debug $1" \ | grep --line-buffered -v --extended-regexp '^[0-9]+\s+.{1,15}DEBUG' \ | grep --line-buffered -v 'processing action' returncode=${PIPESTATUS[0]} - check_witness_files && return $returncode || return 2 + ynh_lxc_pc_witness_files_check --name=$LXC_NAME && return $returncode || return 2 } _PREINSTALL () { @@ -39,7 +39,7 @@ _PREINSTALL () { # Copy the pre-install script into the container. lxc file push "$preinstall_script" "$LXC_NAME/preinstall.sh" # Then execute the script to execute the pre-install commands. - LXC_EXEC "bash /preinstall.sh" + ynh_lxc_pc_exec --name=$LXC_NAME --command="bash /preinstall.sh" fi } @@ -63,7 +63,7 @@ _PREUPGRADE () { # Copy the pre-upgrade script into the container. lxc file push "$preupgrade_script" "$LXC_NAME/preupgrade.sh" # Then execute the script to execute the pre-upgrade commands. - LXC_EXEC "bash /preupgrade.sh" + ynh_lxc_pc_exec --name=$LXC_NAME --command="bash /preupgrade.sh" return $? fi } @@ -111,7 +111,7 @@ _INSTALL_APP () { local ret=$? [ $ret -eq 0 ] && log_debug "Installation successful." || log_error "Installation failed." - if LXC_EXEC "su nobody -s /bin/bash -c \"test -r /var/www/$app_id || test -w /var/www/$app_id || test -x /var/www/$app_id\"" + if ynh_lxc_pc_exec --name=$LXC_NAME --command="su nobody -s /bin/bash -c \"test -r /var/www/$app_id || test -w /var/www/$app_id || test -x /var/www/$app_id\"" then log_error "It looks like anybody can read/enter /var/www/$app_id, which ain't super great from a security point of view ... Config files or other files may contain secrets or information that should in most case not be world-readable. You should remove all 'others' permissions with 'chmod o-rwx', and setup appropriate, exclusive permissions to the appropriate owner/group with chmod/chown." SET_RESULT "failure" install_dir_permissions @@ -126,17 +126,17 @@ _LOAD_SNAPSHOT_OR_INSTALL_APP () { local _install_type="$(path_to_install_type $check_path)" local snapname="snap_${_install_type}install" - if ! LXC_SNAPSHOT_EXISTS $snapname + if ! ynh_lxc_snapshot_exists --name=$LXC_NAME --snapname=$snapname then log_warning "Expected to find an existing snapshot $snapname but it doesn't exist yet .. will attempt to create it" - LOAD_LXC_SNAPSHOT snap0 \ + ynh_lxc_snapshot_load --name=$LXC_NAME --snapname=snap0 \ && _PREINSTALL \ && _INSTALL_APP "path=$check_path" \ - && CREATE_LXC_SNAPSHOT $snapname + && ynh_lxc_pc_snapshot_create --name=$LXC_NAME --snapname=$snapname else # Or uses an existing snapshot log_info "(Reusing existing snapshot $snapname)" \ - && LOAD_LXC_SNAPSHOT $snapname + && ynh_lxc_snapshot_load --name=$LXC_NAME --snapname=$snapname fi } @@ -212,7 +212,7 @@ _VALIDATE_THAT_APP_CAN_BE_ACCESSED () { log_debug "Running curl $check_domain$curl_check_path" # Call cURL to try to access to the URL of the app - LXC_EXEC "curl --location --insecure --silent --show-error \ + ynh_lxc_pc_exec --name=$LXC_NAME --command="curl --location --insecure --silent --show-error \ --header 'Host: $check_domain' \ --resolve $DOMAIN:80:$LXC_IP \ --resolve $DOMAIN:443:$LXC_IP \ @@ -223,7 +223,7 @@ _VALIDATE_THAT_APP_CAN_BE_ACCESSED () { $check_domain$curl_check_path" \ > "$TEST_CONTEXT/curl_print" - LXC_EXEC "cat ./curl_output" > $curl_output + ynh_lxc_pc_exec --name=$LXC_NAME --command="cat ./curl_output" > $curl_output # Analyze the result of curl command if [ $? -ne 0 ] @@ -284,12 +284,12 @@ Page extract:\n$page_extract" > $TEST_CONTEXT/curl_result # If we had a 50x error, try to display service info and logs to help debugging if [[ $curl_error -ne 0 ]] && echo "5" | grep -q "${http_code:0:1}" then - LXC_EXEC "systemctl --all" | grep "$app_id_to_check.*service" - for SERVICE in $(LXC_EXEC "systemctl -all" | grep -o "$app_id_to_check.*service") + ynh_lxc_pc_exec --name=$LXC_NAME --command="systemctl --all" | grep "$app_id_to_check.*service" + for SERVICE in $(ynh_lxc_pc_exec --name=$LXC_NAME --command="systemctl -all" | grep -o "$app_id_to_check.*service") do - LXC_EXEC "journalctl --no-pager --no-hostname -n 30 -u $SERVICE"; + ynh_lxc_pc_exec --name=$LXC_NAME --command="journalctl --no-pager --no-hostname -n 30 -u $SERVICE"; done - LXC_EXEC "tail -v -n 15 \$(find /var/log/{nginx/,php*,$app_id_to_check} -mmin -3)" + ynh_lxc_pc_exec --name=$LXC_NAME --command="tail -v -n 15 \$(find /var/log/{nginx/,php*,$app_id_to_check} -mmin -3)" fi done @@ -356,7 +356,7 @@ TEST_INSTALL () { [ "$install_type" = "private" ] && { start_test "Installation in private mode"; local is_public="0"; } local snapname=snap_${install_type}install - LOAD_LXC_SNAPSHOT snap0 + ynh_lxc_snapshot_load --name=$LXC_NAME --snapname=snap0 _PREINSTALL @@ -370,9 +370,9 @@ TEST_INSTALL () { # Create the snapshot that'll be used by other tests later [ "$install_type" != "private" ] \ - && ! LXC_SNAPSHOT_EXISTS $snapname \ + && ! ynh_lxc_snapshot_exists --name=$LXC_NAME --snapname=$snapname \ && log_debug "Create a snapshot after app install" \ - && CREATE_LXC_SNAPSHOT $snapname + && ynh_lxc_pc_snapshot_create --name=$LXC_NAME --snapname=$snapname # Remove and reinstall the application _REMOVE_APP \ @@ -392,7 +392,7 @@ _TEST_MULTI_INSTANCE () { local check_path="$(default_install_path)" - LOAD_LXC_SNAPSHOT snap0 + ynh_lxc_snapshot_load --name=$LXC_NAME --snapname=snap0 log_small_title "First installation: path=$SUBDOMAIN$check_path" \ && _LOAD_SNAPSHOT_OR_INSTALL_APP "$check_path" \ @@ -436,7 +436,7 @@ TEST_UPGRADE () { cp -a "$package_path" "${package_path}_back" (cd "$package_path"; git checkout --force --quiet "$commit") - LOAD_LXC_SNAPSHOT snap0 + ynh_lxc_snapshot_load --name=$LXC_NAME --snapname=snap0 _PREINSTALL @@ -481,7 +481,7 @@ TEST_PORT_ALREADY_USED () { local check_port="$1" local check_path="$(default_install_path)" - LOAD_LXC_SNAPSHOT snap0 + ynh_lxc_snapshot_load --name=$LXC_NAME --snapname=snap0 # Build a service with netcat for use this port before the app. echo -e "[Service]\nExecStart=/bin/netcat -l -k -p $check_port\n @@ -490,7 +490,7 @@ TEST_PORT_ALREADY_USED () { lxc file push $TEST_CONTEXT/netcat.service $LXC_NAME/etc/systemd/system/netcat.service # Then start this service to block this port. - LXC_EXEC "systemctl enable --now netcat" + ynh_lxc_pc_exec --name=$LXC_NAME --command="systemctl enable --now netcat" _PREINSTALL @@ -530,7 +530,7 @@ TEST_BACKUP_RESTORE () { # Remove the previous residual backups rm -rf $TEST_CONTEXT/ynh_backups - RUN_INSIDE_LXC rm -rf /home/yunohost.backup/archives + ynh_lxc_run_inside --name=$LXC_NAME --command="rm -rf /home/yunohost.backup/archives" # BACKUP # Made a backup if the installation succeed @@ -570,10 +570,10 @@ TEST_BACKUP_RESTORE () { elif [ $j -eq 1 ] then - LOAD_LXC_SNAPSHOT snap0 + ynh_lxc_snapshot_load --name=$LXC_NAME --snapname=snap0 # Remove the previous residual backups - RUN_INSIDE_LXC rm -rf /home/yunohost.backup/archives + ynh_lxc_run_inside --name=$LXC_NAME --command="rm -rf /home/yunohost.backup/archives" # Place the copy of the backup archive in the container. lxc file push -r $TEST_CONTEXT/ynh_backups/archives $LXC_NAME/home/yunohost.backup/ diff --git a/lib/tests_coordination.sh b/lib/tests_coordination.sh index 40510b9..173d5a3 100644 --- a/lib/tests_coordination.sh +++ b/lib/tests_coordination.sh @@ -1,8 +1,9 @@ #!/bin/bash -source lib/lxc.sh +source lib/ynh_lxd +source lib/ynh_lxd_package_check source lib/tests.sh -source lib/witness.sh +source /usr/share/yunohost/helpers readonly complete_log="./Complete-${WORKER_ID}.log" @@ -254,16 +255,16 @@ run_all_tests() { # Reset and create a fresh container to work with check_lxd_setup - LXC_RESET - LXC_CREATE + ynh_lxc_reset --name=$LXC_NAME + ynh_lxc_pc_create --image=$LXC_BASE --name=$LXC_NAME # Be sure that the container is running - LXC_EXEC "true" + ynh_lxc_pc_exec --name=$LXC_NAME --command="true" # Print the version of YunoHost from the LXC container log_small_title "YunoHost versions" - LXC_EXEC "yunohost --version" - LXC_EXEC "yunohost --version --output-as json" | jq -r .yunohost.version >> $TEST_CONTEXT/ynh_version - LXC_EXEC "yunohost --version --output-as json" | jq -r .yunohost.repo >> $TEST_CONTEXT/ynh_branch + ynh_lxc_pc_exec --name=$LXC_NAME --command="yunohost --version" + ynh_lxc_pc_exec --name=$LXC_NAME --command="yunohost --version --output-as json" | jq -r .yunohost.version >> $TEST_CONTEXT/ynh_version + ynh_lxc_pc_exec --name=$LXC_NAME --command="yunohost --version --output-as json" | jq -r .yunohost.repo >> $TEST_CONTEXT/ynh_branch echo $ARCH > $TEST_CONTEXT/architecture echo $app_id > $TEST_CONTEXT/app_id @@ -349,7 +350,7 @@ TEST_LAUNCHER () { # End the timer for the test stop_timer 2 - LXC_STOP $LXC_NAME + ynh_lxc_stop --name=$LXC_NAME # Update the lock file with the date of the last finished test. # $$ is the PID of package_check itself. diff --git a/lib/witness.sh b/lib/witness.sh deleted file mode 100644 index ea31590..0000000 --- a/lib/witness.sh +++ /dev/null @@ -1,90 +0,0 @@ - -set_witness_files () { - # Create files to check if the remove script does not remove them accidentally - log_debug "Create witness files..." - - create_witness_file () { - [ "$2" = "file" ] && local action="touch" || local action="mkdir -p" - RUN_INSIDE_LXC $action $1 - } - - # Nginx conf - create_witness_file "/etc/nginx/conf.d/$DOMAIN.d/witnessfile.conf" file - create_witness_file "/etc/nginx/conf.d/$SUBDOMAIN.d/witnessfile.conf" file - - # /etc - create_witness_file "/etc/witnessfile" file - - # /opt directory - create_witness_file "/opt/witnessdir" directory - - # /var/www directory - create_witness_file "/var/www/witnessdir" directory - - # /home/yunohost.app/ - create_witness_file "/home/yunohost.app/witnessdir" directory - - # /var/log - create_witness_file "/var/log/witnessfile" file - - # Config fpm - create_witness_file "/etc/php/$DEFAULT_PHP_VERSION/fpm/pool.d/witnessfile.conf" file - - # Config logrotate - create_witness_file "/etc/logrotate.d/witnessfile" file - - # Config systemd - create_witness_file "/etc/systemd/system/witnessfile.service" file - - # Database - RUN_INSIDE_LXC mysqladmin --wait status > /dev/null 2>&1 - echo "CREATE DATABASE witnessdb" | RUN_INSIDE_LXC mysql --wait > /dev/null 2>&1 -} - -check_witness_files () { - # Check all the witness files, to verify if them still here - - check_file_exist () { - if RUN_INSIDE_LXC test ! -e "$1" - then - log_error "The file $1 is missing ! Something gone wrong !" - SET_RESULT "failure" witness - fi - } - - # Nginx conf - check_file_exist "/etc/nginx/conf.d/$DOMAIN.d/witnessfile.conf" - check_file_exist "/etc/nginx/conf.d/$SUBDOMAIN.d/witnessfile.conf" - - # /etc - check_file_exist "/etc/witnessfile" - - # /opt directory - check_file_exist "/opt/witnessdir" - - # /var/www directory - check_file_exist "/var/www/witnessdir" - - # /home/yunohost.app/ - check_file_exist "/home/yunohost.app/witnessdir" - - # /var/log - check_file_exist "/var/log/witnessfile" - - # Config fpm - check_file_exist "/etc/php/$DEFAULT_PHP_VERSION/fpm/pool.d/witnessfile.conf" - - # Config logrotate - check_file_exist "/etc/logrotate.d/witnessfile" - - # Config systemd - check_file_exist "/etc/systemd/system/witnessfile.service" - - # Database - if ! RUN_INSIDE_LXC mysqlshow witnessdb > /dev/null 2>&1 - then - log_error "The database witnessdb is missing ! Something gone wrong !" - SET_RESULT "failure" witness - return 1 - fi -} diff --git a/lib/ynh_lxd b/lib/ynh_lxd new file mode 100644 index 0000000..d8e0bac --- /dev/null +++ b/lib/ynh_lxd @@ -0,0 +1,566 @@ +#!/bin/bash + +#================================================= +# LXD HELPERS +#================================================= + +# Check if a LXC container exists +# +# usage: ynh_lxc_exists --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_exists () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if ! lxc list --format json | jq -e --arg name $name '.[] | select(.name==$name) | .name' >/dev/null + then + return 1 + else + return 0 + fi +} + +# Return LXC container status +# +# usage: ynh_lxc_status --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_status () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if ynh_lxc_exists --name=$name + then + lxc list --format json | jq -r --arg name $name '.[] | select(.name==$name) | .state | .status' + fi +} + +# Check if an LXC container is running +# +# usage: ynh_lxc_is_started --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_is_started () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if [ "$(ynh_lxc_status --name=$name)" == Running ] + then + return 0 + else + return 1 + fi +} + +# Check if an LXC container is stopped +# +# usage: ynh_lxc_is_stopped --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_is_stopped () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if [ "$(ynh_lxc_status --name=$name)" == Stopped ] + then + return 0 + else + return 1 + fi +} + +# Start an LXC container +# +# usage: ynh_lxc_start --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_start () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # If the container exists + if ynh_lxc_exists --name=$name + then + if ! ynh_lxc_is_started --name=$name + then + lxc start $name + fi + fi +} + +# Stopping an LXC container +# +# usage: ynh_lxc_stop --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_stop () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # If the container exists + if ynh_lxc_exists --name=$name + then + ynh_print_info --message="Stopping LXC $name" + wait_period=0 + while ! ynh_lxc_is_stopped --name=$name + do + lxc stop $name + wait_period=$(($wait_period+10)) + if [ $wait_period -gt 30 ];then + break + else + sleep 1 + fi + done + + # If the command times out, then add the option --force + wait_period=0 + while ! ynh_lxc_is_stopped --name=$name + do + lxc stop $name --force + wait_period=$(($wait_period+10)) + if [ $wait_period -gt 30 ];then + break + else + sleep 5 + fi + done + fi +} + +# Run a command inside an LXC container +# +# usage: ynh_lxc_run_inside --name=name --command=command +# | arg: -n, --name= - name of the LXC +# | arg: -c, --command= - command to execute +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_run_inside () { + # Declare an array to define the options of this helper. + local legacy_args=nc + local -A args_array=([n]=name= [c]=command=) + local name + local command + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + lxc exec $name -- /bin/bash -c "$command" +} + +# Check an LXC container can start +# +# usage: ynh_lxc_check_container_start --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_check_container_start () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + ynh_print_info --message="Test du conteneur $name" + ynh_lxc_start --name=$name # Démarre le conteneur + + wait_period=0 + while ! ynh_lxc_is_started --name=$name + do + wait_period=$(($wait_period+10)) + if [ $wait_period -gt 20 ];then + break + else + sleep 5 + fi + done + + ynh_lxc_is_started --name=$name +} + +# Restart a container +# +# usage: _ynh_lxc_restart_container --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +_ynh_lxc_restart_container () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + ynh_lxc_stop --name=$name + ynh_lxc_start --name=$name +} + +# Keep sure the LXC is started +# +# usage: _ynh_lxc_start_and_wait --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +_ynh_lxc_start_and_wait () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # Try to start the container 3 times. + ynh_lxc_start --name=$name + local max_try=3 + local i=0 + while [ $i -lt $max_try ] + do + i=$(( i +1 )) + local failstart=0 + + # Wait for container to start, we are using systemd to check this, + # for the sake of brevity. + for j in $(seq 1 10); do + if ynh_lxc_run_inside --name=$name --command="systemctl isolate multi-user.target >/dev/null 2>/dev/null" + then + break + fi + + if [ "$j" == "10" ]; then + log_debug 'Failed to start the container ... restarting ...' + failstart=1 + + _ynh_lxc_restart_container --name="$name" + fi + + sleep 1s + done + + # Wait for container to access the internet + for j in $(seq 1 10); do + if ynh_lxc_run_inside --name=$name --command="curl -s http://wikipedia.org > /dev/null 2>/dev/null" + then + break + fi + + if [ "$j" == "10" ]; then + log_debug 'Failed to access the internet ... restarting' + failstart=1 + + _ynh_lxc_restart_container --name="$name" + fi + + sleep 1s + done + + # Has started and has access to the internet + if [ $failstart -eq 0 ] + then + break + fi + + # Fail if the container failed to start + if [ $i -eq $max_try ] && [ $failstart -eq 1 ] + then + log_error "The container miserably failed to start or to connect to the internet" + lxc info --show-log $name + return 1 + fi + done + + LXC_IP=$(ynh_lxc_run_inside --name=$name --command="hostname -I | cut -d' ' -f1 | grep -E -o \"\<[0-9.]{8,}\>\"") +} + +# Launch a new LXC from an image +# +# usage: ynh_lxc_launch --image=image --name=name +# | arg: -i, --image= - image to create from +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_launch (){ + # Declare an array to define the options of this helper. + local legacy_args=in + local -A args_array=([i]=image= [n]=name=) + local image + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if lxc remote list | grep -q "yunohost" && lxc image list yunohost:$image | grep -q -w $image; then + lxc launch yunohost:$image $name \ + -c security.nesting=true \ + -c security.privileged=true \ + -c limits.memory=80% \ + -c limits.cpu.allowance=80% | tee -a /proc/self/fd/3 + # Check if we can launch container from a local image + elif lxc image list $image | grep -q -w $image; then + lxc launch $image $name \ + -c security.nesting=true \ + -c security.privileged=true \ + -c limits.memory=80% \ + -c limits.cpu.allowance=80% | tee -a /proc/self/fd/3 + else + log_critical "Can't find base image $image" + fi +} + +# Delete a lxc container +# +# usage: ynh_lxc_delete --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_delete () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if ynh_lxc_exists --name=$name + then + lxc delete $name --force + else + return 1 + fi +} + +# Clean the swapfiles of an LXC container +# +# usage: ynh_lxc_swapfiles_clean --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_swapfiles_clean () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + _ynh_lxc_start_and_wait --name=$name + + ynh_lxc_run_inside --name=$name --command='for swapfile in $(ls /swap_* 2>/dev/null); do swapoff $swapfile; done' + ynh_lxc_run_inside --name=$name --command='for swapfile in $(ls /swap_* 2>/dev/null); do rm -f $swapfile; done' +} + +# Check if a snapshot exist for an LXC container +# +# usage: ynh_lxc_snapshot_exists --name=name --snapname=snapname +# | arg: -n, --name= - name of the LXC +# | arg: -s, --snapname= - name of the snapshot +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_snapshot_exists () { + # Declare an array to define the options of this helper. + local legacy_args=ns + local -A args_array=([n]=name= [s]=snapname=) + local name + local snapname + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # If the container exists + if ynh_lxc_exists --name=$name + then + if lxc list --format json | jq -e --arg name $name --arg snapname $snapname '.[] | select(.name==$name) | .snapshots' >/dev/null + then + if lxc list --format json | jq -e --arg name $name --arg snapname $snapname '.[] | select(.name==$name) | .snapshots[] | select(.name==$snapname)' >/dev/null + then + return 0 + else + return 1 + fi + else + return 1 + fi + fi +} + +# Create a snapshot of an LXC container +# +# usage: ynh_lxc_snapshot_create --name=name --snapname=snapname +# | arg: -n, --name= - name of the LXC +# | arg: -s, --snapname= - name of the snapshot +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_snapshot_create () { + # Declare an array to define the options of this helper. + local legacy_args=ns + local -A args_array=([n]=name= [s]=snapname=) + local name + local snapname + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # Remove swap files to avoid killing the CI with huge snapshots. + ynh_lxc_swapfiles_clean --name=$name + + ynh_lxc_stop --name=$name + + # Check if the snapshot already exist + if ! ynh_lxc_snapshot_exists --name=$name --snapname="$snapname" + then + log_info "(Creating snapshot $snapname ...)" + lxc snapshot $name $snapname + else + log_info "(Recreating snapshot $snapname ...)" + lxc snapshot $name $snapname --reuse + fi +} + +# Delete a snapshot of an LXC container +# +# usage: ynh_lxc_snapshot_delete --name=name --snapname=snapname +# | arg: -n, --name= - name of the LXC +# | arg: -s, --snapname= - name of the snapshot +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_snapshot_delete () { + # Declare an array to define the options of this helper. + local legacy_args=ns + local -A args_array=([n]=name= [s]=snapname=) + local name + local snapname + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if ynh_lxc_snapshot_exists --name="$name" --snapname="$snapname" + then + lxc delete $name/$snapname + return 0 + else + return 1 + fi +} + +# Load a snapshot of an LXC container +# +# usage: ynh_lxc_snapshot_load --name=name --snapname=snapname +# | arg: -n, --name= - name of the LXC +# | arg: -s, --snapname= - name of the snapshot +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_snapshot_load () { + # Declare an array to define the options of this helper. + local legacy_args=ns + local -A args_array=([n]=name= [s]=snapname=) + local name + local snapname + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if ynh_lxc_snapshot_exists --name="$name" --snapname="$snapname" + then + log_debug "Loading snapshot $snapname ..." + + # Remove swap files before restoring the snapshot. + ynh_lxc_swapfiles_clean --name=$name + + ynh_lxc_stop --name=$name + + lxc restore $name $snapname + _ynh_lxc_start_and_wait --name=$name + else + return 1 + fi +} + +# Clone an LXC container +# +# usage: ynh_lxc_clone --source=source --destination=destination +# | arg: -s, --source= - source LXC +# | arg: -d, --destination= - destination LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_clone () { + # Declare an array to define the options of this helper. + local legacy_args=sd + local -A args_array=([s]=source= [d]=destination=) + local source + local destination + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if ynh_lxc_exists --name=$destination + then + ynh_print_info --message="Deleting LXC container $destination" + ynh_secure_remove --file="/var/lib/lxd/snapshots/$destination/snap0.tar.gz" + ynh_lxc_reset --name=$destination + fi + + ynh_print_info --message="Cloning LXC container from $source to $destination" + lxc copy "$source" "$destination" + + ynh_lxc_check_container_start --name=$destination + STATUS=$? + if [ $STATUS -eq 1 ]; then + ynh_print_info --message="LXC container $destination is broken." + else + ynh_print_info --message=" LXC container $destination is working." + ynh_print_info --message= "Creating snapshot of LXC container $destination" + ynh_lxc_snapshot_create --name="$destination" --snapname="snap0" + fi + return $STATUS +} + +# Reset an LXC container +# +# usage: ynh_lxc_reset --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_reset () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # If the container exists + if ynh_lxc_exists --name=$name + then + # Remove swap files before deleting the container + ynh_lxc_swapfiles_clean --name=$name + ynh_lxc_stop --name=$name + local current_storage=$(lxc list $name --format json --columns b | jq '.[].expanded_devices.root.pool') + swapoff "$(lxc storage get $current_storage source)/containers/$name/rootfs/swap" 2>/dev/null + ynh_lxc_delete --name=$name + fi +} diff --git a/lib/ynh_lxd_package_check b/lib/ynh_lxd_package_check new file mode 100644 index 0000000..c21cbca --- /dev/null +++ b/lib/ynh_lxd_package_check @@ -0,0 +1,245 @@ +#!/bin/bash + +#================================================= +# PACKAGE_CHECK HELPERS +#================================================= + +# Start an LXC and execute a command in it +# +# usage: ynh_lxc_pc_exec --name=name --command=command +# | arg: -n, --name= - name of the LXC +# | arg: -c, --command= - command to execute +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_pc_exec () { + # Declare an array to define the options of this helper. + local legacy_args=nc + local -A args_array=([n]=name= [c]=command=) + local name + local command + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + _ynh_lxc_start_and_wait --name=$name + + start_timer + + # Execute the command given in argument in the container and log its results. + lxc exec $name --env PACKAGE_CHECK_EXEC=1 -t -- /bin/bash -c "$command" | tee -a "$complete_log" $current_test_log + + # Store the return code of the command + local returncode=${PIPESTATUS[0]} + + log_debug "Return code: $returncode" + + stop_timer 1 + # Return the exit code of the ssh command + return $returncode +} + +# Create a witness in an LXC container +# +# usage: ynh_lxc_pc_witness_file_create --name=name --witness=witness --type=type +# | arg: -n, --name= - name of the LXC +# | arg: -w, --witness= - witness to create +# | arg: -t, --type= - type of witness, can be file or directory +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_pc_witness_file_create () { + # Declare an array to define the options of this helper. + local legacy_args=nwt + local -A args_array=([n]=name= [w]=witness= [t]=type=) + local name + local witness + local type + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + [ "$type" = "file" ] && local action="touch" || local action="mkdir -p" + ynh_lxc_run_inside --name=$name --command="$action $witness" +} + +# Set witness in an LXC container +# +# usage: ynh_lxc_pc_witness_files_set --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_pc_witness_files_set () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # Create files to check if the remove script does not remove them accidentally + log_debug "Create witness files..." + + # Nginx conf + ynh_lxc_pc_witness_file_create --name=$name --witness="/etc/nginx/conf.d/$DOMAIN.d/witnessfile.conf" --type=file + ynh_lxc_pc_witness_file_create --name=$name --witness="/etc/nginx/conf.d/$SUBDOMAIN.d/witnessfile.conf" --type=file + + # /etc + ynh_lxc_pc_witness_file_create --name=$name --witness="/etc/witnessfile" --type=file + + # /opt directory + ynh_lxc_pc_witness_file_create --name=$name --witness="/opt/witnessdir" --type=directory + + # /var/www directory + ynh_lxc_pc_witness_file_create --name=$name --witness="/var/www/witnessdir" --type=directory + + # /home/yunohost.app/ + ynh_lxc_pc_witness_file_create --name=$name --witness="/home/yunohost.app/witnessdir" --type=directory + + # /var/log + ynh_lxc_pc_witness_file_create --name=$name --witness="/var/log/witnessfile" --type=file + + # Config fpm + ynh_lxc_pc_witness_file_create --name=$name --witness="/etc/php/$DEFAULT_PHP_VERSION/fpm/pool.d/witnessfile.conf" --type=file + + # Config logrotate + ynh_lxc_pc_witness_file_create --name=$name --witness="/etc/logrotate.d/witnessfile" --type=file + + # Config systemd + ynh_lxc_pc_witness_file_create --name=$name --witness="/etc/systemd/system/witnessfile.service" --type=file + + # Database + ynh_lxc_run_inside --name=$name --command="mysqladmin --wait status > /dev/null 2>&1" + ynh_lxc_run_inside --name=$name --command="echo \"CREATE DATABASE witnessdb\" | mysql --wait > /dev/null 2>&1" +} + +# Check if a witness exists in an LXC container +# +# usage: ynh_lxc_pc_witness_file_check --name=name --witness=witness +# | arg: -n, --name= - name of the LXC +# | arg: -w, --witness= - witness to create +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_pc_witness_file_check () { + # Declare an array to define the options of this helper. + local legacy_args=nw + local -A args_array=([n]=name= [w]=witness=) + local name + local witness + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if ynh_lxc_run_inside --name=$name --command="test ! -e \"$witness\"" + then + log_error "The file $witness is missing ! Something gone wrong !" + SET_RESULT "failure" witness + fi +} + +# Check witness in an LXC container +# +# usage: ynh_lxc_pc_witness_files_check --name=name +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_pc_witness_files_check () { + # Declare an array to define the options of this helper. + local legacy_args=n + local -A args_array=([n]=name=) + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + # Check all the witness files, to verify if them still here + + # Nginx conf + ynh_lxc_pc_witness_file_check --name=$name --witness="/etc/nginx/conf.d/$DOMAIN.d/witnessfile.conf" + ynh_lxc_pc_witness_file_check --name=$name --witness="/etc/nginx/conf.d/$SUBDOMAIN.d/witnessfile.conf" + + # /etc + ynh_lxc_pc_witness_file_check --name=$name --witness="/etc/witnessfile" + + # /opt directory + ynh_lxc_pc_witness_file_check --name=$name --witness="/opt/witnessdir" + + # /var/www directory + ynh_lxc_pc_witness_file_check --name=$name --witness="/var/www/witnessdir" + + # /home/yunohost.app/ + ynh_lxc_pc_witness_file_check --name=$name --witness="/home/yunohost.app/witnessdir" + + # /var/log + ynh_lxc_pc_witness_file_check --name=$name --witness="/var/log/witnessfile" + + # Config fpm + ynh_lxc_pc_witness_file_check --name=$name --witness="/etc/php/$DEFAULT_PHP_VERSION/fpm/pool.d/witnessfile.conf" + + # Config logrotate + ynh_lxc_pc_witness_file_check --name=$name --witness="/etc/logrotate.d/witnessfile" + + # Config systemd + ynh_lxc_pc_witness_file_check --name=$name --witness="/etc/systemd/system/witnessfile.service" + + # Database + if ! ynh_lxc_run_inside --name=$name --command="mysqlshow witnessdb > /dev/null 2>&1" + then + log_error "The database witnessdb is missing ! Something gone wrong !" + SET_RESULT "failure" witness + return 1 + fi +} + +# Create a new LXC from an image +# +# usage: ynh_lxc_pc_create --image=image --name=name +# | arg: -i, --image= - image to create from +# | arg: -n, --name= - name of the LXC +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_pc_create () { + # Declare an array to define the options of this helper. + local legacy_args=in + local -A args_array=([i]=image= [n]=name=) + local image + local name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + log_info "Launching new LXC $name ..." + # Check if we can launch container from YunoHost remote image + ynh_lxc_launch --image=$image --name=$name + + pipestatus="${PIPESTATUS[0]}" + location=$(lxc list --format json | jq -e --arg name $name '.[] | select(.name==$name) | .location' | tr -d '"') + [[ "$location" != "none" ]] && log_info "... on $location" + + [[ "$pipestatus" -eq 0 ]] || exit 1 + + _ynh_lxc_start_and_wait --name=$name + ynh_lxc_pc_witness_files_set --name=$name + ynh_lxc_snapshot_create --name=$name --snapname=snap0 +} + +# Create a snapshot of an LXC container +# +# usage: ynh_lxc_pc_snapshot_create --name=name --snapname=snapname +# | arg: -n, --name= - name of the LXC +# | arg: -s, --snapname= - name of the snapshot +# +# Requires YunoHost version *.*.* or higher. +ynh_lxc_pc_snapshot_create () { + # Declare an array to define the options of this helper. + local legacy_args=ns + local -A args_array=([n]=name= [s]=snapname=) + local name + local snapname + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + start_timer + + # Check all the witness files, to verify if them still here + ynh_lxc_pc_witness_files_check --name=$name >&2 + + ynh_lxc_snapshot_create --name=$name --snapname=$snapname + + _ynh_lxc_start_and_wait --name=$name + + stop_timer 1 +} diff --git a/package_check.sh b/package_check.sh index 674415c..e75b43e 100755 --- a/package_check.sh +++ b/package_check.sh @@ -135,7 +135,7 @@ parse_args function cleanup() { trap '' SIGINT # Disable ctrl+c in this function - LXC_RESET + ynh_lxc_reset --name=$LXC_NAME [ -n "$TEST_CONTEXT" ] && rm -rf "$TEST_CONTEXT" [ -n "$lock_file" ] && rm -f "$lock_file"