From e02664cb3324a743725da4c303f490156e175fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= Date: Sat, 25 Jan 2020 22:33:33 +0100 Subject: [PATCH] Add basic package skeleton --- README.md | 9 + check_process | 31 ++++ conf/nginx.conf | 17 ++ conf/sudoers | 5 + conf/systemd.service | 10 ++ manifest.json | 49 +++++ scripts/_common.sh | 419 +++++++++++++++++++++++++++++++++++++++++++ scripts/backup | 24 +++ scripts/install | 91 ++++++++++ scripts/remove | 47 +++++ scripts/restore | 66 +++++++ scripts/upgrade | 60 +++++++ 12 files changed, 828 insertions(+) create mode 100644 README.md create mode 100644 check_process create mode 100644 conf/nginx.conf create mode 100644 conf/sudoers create mode 100644 conf/systemd.service create mode 100644 manifest.json create mode 100644 scripts/_common.sh create mode 100644 scripts/backup create mode 100644 scripts/install create mode 100644 scripts/remove create mode 100644 scripts/restore create mode 100644 scripts/upgrade diff --git a/README.md b/README.md new file mode 100644 index 0000000..910c533 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Octoprint for YunoHost + +Octoprint is a webserver for managing connected 3D printer. + +## Links +* [Octoprint website](https://octoprint.org/) + +## Notes +* This package is heavily inspired from [Home Assistant package](https://github.com/YunoHost-Apps/homeassistant_ynh) diff --git a/check_process b/check_process new file mode 100644 index 0000000..fd47f5b --- /dev/null +++ b/check_process @@ -0,0 +1,31 @@ +;; Test complet + ; Manifest + domain="domain.tld" (DOMAIN) + is_public=1 (PUBLIC|public=1|private=0) + ; Checks + pkg_linter=1 + setup_sub_dir=0 + setup_root=1 + setup_nourl=0 + setup_private=0 + setup_public=0 + upgrade=1 + backup_restore=1 + multi_instance=0 + incorrect_path=0 + port_already_use=1 + change_url=0 +;;; Levels + Level 1=auto + Level 2=auto + Level 3=auto + Level 4=na + Level 5=1 + Level 6=auto + Level 7=auto + Level 8=0 + Level 9=0 + Level 10=0 +;;; Options +Email= +Notification=none diff --git a/conf/nginx.conf b/conf/nginx.conf new file mode 100644 index 0000000..b61db04 --- /dev/null +++ b/conf/nginx.conf @@ -0,0 +1,17 @@ +location / { + + if ($scheme = http) { + rewrite ^ https://$server_name$request_uri? permanent; + } + + proxy_pass http://localhost:__PORT__; + proxy_set_header Host $host; + proxy_redirect http:// https://; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + # Include SSOWAT user panel. + include conf.d/yunohost_panel.conf.inc; +} diff --git a/conf/sudoers b/conf/sudoers new file mode 100644 index 0000000..4715985 --- /dev/null +++ b/conf/sudoers @@ -0,0 +1,5 @@ +# Grant sudo permissions to the user to manage his own systemd service +octoprint ALL=(ALL) NOPASSWD: /bin/systemctl stop octoprint@octoprint.service +octoprint ALL=(ALL) NOPASSWD: /bin/systemctl start octoprint@octoprint.service +octoprint ALL=(ALL) NOPASSWD: /bin/systemctl restart octoprint@octoprint.service +octoprint ALL=(ALL) NOPASSWD: /bin/systemctl status octoprint@octoprint.service diff --git a/conf/systemd.service b/conf/systemd.service new file mode 100644 index 0000000..da87fe1 --- /dev/null +++ b/conf/systemd.service @@ -0,0 +1,10 @@ +[Unit] +Description=Octoprint +After=network-online.target + +[Service] +Type=simple +User=octoprint +ExecStart=/opt/yunohost/octoprint/bin/octoprint serve +[Install] +WantedBy=multi-user.target diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..0b5b07e --- /dev/null +++ b/manifest.json @@ -0,0 +1,49 @@ +{ + "name": "Octoprint", + "id": "octoprint", + "packaging_format": 1, + "description": { + "en": "3D printing webserver", + "fr": "Serveur d'impression 3D" + }, + "version": "1.0.0", + "url": "https://github.com/Tropicao/octoprint_ynh", + "license": "Apache-2.0", + "maintainer": { + "name": "Tropicao", + "email": "alexis.lothore@gmail.com" + }, + "requirements": { + "yunohost": ">= 3.1" + }, + "multi_instance": false, + "services": [ + "nginx" + ], + "arguments": { + "install": [ + { + "name": "domain", + "type": "domain", + "ask": { + "en": "Choose a domain for Octoprint", + "fr": "Choisissez un domaine pour Octoprint" + }, + "example": "domain.org" + }, + { + "name": "is_public", + "type": "boolean", + "ask": { + "en": "Should this application be public ? (if so, your 3D printer will be accessible by anyone on internet)", + "fr": "Est-ce que cette application doit être visible publiquement ? (dans ce cas, n'importe qui sur internet pourra utiliser votre imprimante)" + }, + "help": { + "en": "If public, your 3D printer will be accessible by anyone on internet", + "fr": "Dans ce cas, n'importe qui sur internet pourra utiliser votre imprimante" + }, + "default": true + } + ] + } +} diff --git a/scripts/_common.sh b/scripts/_common.sh new file mode 100644 index 0000000..b32d5cf --- /dev/null +++ b/scripts/_common.sh @@ -0,0 +1,419 @@ +# +# Common variables & functions +# + +# Package dependencies +PKG_DEPENDENCIES="python3 python3-venv python3-pip build-essential libssl-dev libffi-dev python3-dev" + +# Check if directory/file already exists (path in argument) +myynh_check_path () { + [ -z "$1" ] && ynh_die "No argument supplied" + [ ! -e "$1" ] || ynh_die "$1 already exists" +} + +# Create directory only if not already exists (path in argument) +myynh_create_dir () { + [ -z "$1" ] && ynh_die "No argument supplied" + [ -d "$1" ] || mkdir -p "$1" +} + +# Check if enough disk space available on backup storage +myynh_check_disk_space () { + file_to_analyse=$1 + backup_size=$(du --summarize "$1" | cut -f1) + free_space=$(df --output=avail "/home/yunohost.backup" | sed 1d) + if [ $free_space -le $backup_size ]; then + WARNING echo "Not enough backup disk space for: $1" + WARNING echo "Space available: $(HUMAN_SIZE $free_space)" + ynh_die "Space needed: $(HUMAN_SIZE $backup_size)" + fi +} + +# Clean & copy files needed to final folder +myynh_clean_source () { + find "$TMPDIR" -type f -name ".htaccess" | xargs rm + [ -e "$TMPDIR/.gitignore" ] && rm -r "$TMPDIR/.gitignore" +} + +#================================================= +# FUTURE YUNOHOST HELPERS - TO BE REMOVED LATER +#================================================= + +# Delete a file checksum from the app settings +# +# $app should be defined when calling this helper +# +# usage: ynh_remove_file_checksum file +# | arg: file - The file for which the checksum will be deleted +ynh_delete_file_checksum () { + local checksum_setting_name=checksum_${1//[\/ ]/_} # Replace all '/' and ' ' by '_' + ynh_app_setting_delete $app $checksum_setting_name +} + +# Execute a command as another user +# usage: exec_as USER COMMAND [ARG ...] +exec_as() { + local USER=$1 + shift 1 + + if [[ $USER = $(whoami) ]]; then + eval "$@" + else + sudo -u "$USER" "$@" + fi +} + +# Internal helper design to allow helpers to use getopts to manage their arguments +# +# example: function my_helper() +# { +# declare -Ar args_array=( [a]=arg1= [b]=arg2= [c]=arg3 ) +# local arg1 +# local arg2 +# local arg3 +# ynh_handle_getopts_args "$@" +# +# [...] +# } +# my_helper --arg1 "val1" -b val2 -c +# +# usage: ynh_handle_getopts_args "$@" +# | arg: $@ - Simply "$@" to tranfert all the positionnal arguments to the function +# +# This helper need an array, named "args_array" with all the arguments used by the helper +# that want to use ynh_handle_getopts_args +# Be carreful, this array has to be an associative array, as the following example: +# declare -Ar args_array=( [a]=arg1 [b]=arg2= [c]=arg3 ) +# Let's explain this array: +# a, b and c are short options, -a, -b and -c +# arg1, arg2 and arg3 are the long options associated to the previous short ones. --arg1, --arg2 and --arg3 +# For each option, a short and long version has to be defined. +# Let's see something more significant +# declare -Ar args_array=( [u]=user [f]=finalpath= [d]=database ) +# +# NB: Because we're using 'declare' without -g, the array will be declared as a local variable. +# +# Please keep in mind that the long option will be used as a variable to store the values for this option. +# For the previous example, that means that $finalpath will be fill with the value given as argument for this option. +# +# Also, in the previous example, finalpath has a '=' at the end. That means this option need a value. +# So, the helper has to be call with --finalpath /final/path, --finalpath=/final/path or -f /final/path, the variable $finalpath will get the value /final/path +# If there's many values for an option, -f /final /path, the value will be separated by a ';' $finalpath=/final;/path +# For an option without value, like --user in the example, the helper can be called only with --user or -u. $user will then get the value 1. +# +# To keep a retrocompatibility, a package can still call a helper, using getopts, with positional arguments. +# The "legacy mode" will manage the positional arguments and fill the variable in the same order than they are given in $args_array. +# e.g. for `my_helper "val1" val2`, arg1 will be filled with val1, and arg2 with val2. +ynh_handle_getopts_args () { + # Manage arguments only if there's some provided + set +x + if [ $# -ne 0 ] + then + # Store arguments in an array to keep each argument separated + local arguments=("$@") + + # For each option in the array, reduce to short options for getopts (e.g. for [u]=user, --user will be -u) + # And built parameters string for getopts + # ${!args_array[@]} is the list of all keys in the array (A key is 'u' in [u]=user, user is a value) + local getopts_parameters="" + local key="" + for key in "${!args_array[@]}" + do + # Concatenate each keys of the array to build the string of arguments for getopts + # Will looks like 'abcd' for -a -b -c -d + # If the value of a key finish by =, it's an option with additionnal values. (e.g. --user bob or -u bob) + # Check the last character of the value associate to the key + if [ "${args_array[$key]: -1}" = "=" ] + then + # For an option with additionnal values, add a ':' after the letter for getopts. + getopts_parameters="${getopts_parameters}${key}:" + else + getopts_parameters="${getopts_parameters}${key}" + fi + # Check each argument given to the function + local arg="" + # ${#arguments[@]} is the size of the array + for arg in `seq 0 $(( ${#arguments[@]} - 1 ))` + do + # And replace long option (value of the key) by the short option, the key itself + # (e.g. for [u]=user, --user will be -u) + # Replace long option with = + arguments[arg]="${arguments[arg]//--${args_array[$key]}/-${key} }" + # And long option without = + arguments[arg]="${arguments[arg]//--${args_array[$key]%=}/-${key}}" + done + done + + # Read and parse all the arguments + # Use a function here, to use standart arguments $@ and be able to use shift. + parse_arg () { + # Read all arguments, until no arguments are left + while [ $# -ne 0 ] + do + # Initialize the index of getopts + OPTIND=1 + # Parse with getopts only if the argument begin by -, that means the argument is an option + # getopts will fill $parameter with the letter of the option it has read. + local parameter="" + getopts ":$getopts_parameters" parameter || true + + if [ "$parameter" = "?" ] + then + ynh_die "Invalid argument: -${OPTARG:-}" + elif [ "$parameter" = ":" ] + then + ynh_die "-$OPTARG parameter requires an argument." + else + local shift_value=1 + # Use the long option, corresponding to the short option read by getopts, as a variable + # (e.g. for [u]=user, 'user' will be used as a variable) + # Also, remove '=' at the end of the long option + # The variable name will be stored in 'option_var' + local option_var="${args_array[$parameter]%=}" + # If this option doesn't take values + # if there's a '=' at the end of the long option name, this option takes values + if [ "${args_array[$parameter]: -1}" != "=" ] + then + # 'eval ${option_var}' will use the content of 'option_var' + eval ${option_var}=1 + else + # Read all other arguments to find multiple value for this option. + # Load args in a array + local all_args=("$@") + + # If the first argument is longer than 2 characters, + # There's a value attached to the option, in the same array cell + if [ ${#all_args[0]} -gt 2 ]; then + # Remove the option and the space, so keep only the value itself. + all_args[0]="${all_args[0]#-${parameter} }" + # Reduce the value of shift, because the option has been removed manually + shift_value=$(( shift_value - 1 )) + fi + + # Then read the array value per value + for i in `seq 0 $(( ${#all_args[@]} - 1 ))` + do + # If this argument is an option, end here. + if [ "${all_args[$i]:0:1}" == "-" ] || [ -z "${all_args[$i]}" ] + then + # Ignore the first value of the array, which is the option itself + if [ "$i" -ne 0 ]; then + break + fi + else + # Declare the content of option_var as a variable. + eval ${option_var}="" + # Else, add this value to this option + # Each value will be separated by ';' + if [ -n "${!option_var}" ] + then + # If there's already another value for this option, add a ; before adding the new value + eval ${option_var}+="\;" + fi + eval ${option_var}+=\"${all_args[$i]}\" + shift_value=$(( shift_value + 1 )) + fi + done + fi + fi + + # Shift the parameter and its argument(s) + shift $shift_value + done + } + + # LEGACY MODE + # Check if there's getopts arguments + if [ "${arguments[0]:0:1}" != "-" ] + then + # If not, enter in legacy mode and manage the arguments as positionnal ones. + echo "! Helper used in legacy mode !" + for i in `seq 0 $(( ${#arguments[@]} -1 ))` + do + # Use getopts_parameters as a list of key of the array args_array + # Remove all ':' in getopts_parameters + getopts_parameters=${getopts_parameters//:} + # Get the key from getopts_parameters, by using the key according to the position of the argument. + key=${getopts_parameters:$i:1} + # Use the long option, corresponding to the key, as a variable + # (e.g. for [u]=user, 'user' will be used as a variable) + # Also, remove '=' at the end of the long option + # The variable name will be stored in 'option_var' + local option_var="${args_array[$key]%=}" + + # Store each value given as argument in the corresponding variable + # The values will be stored in the same order than $args_array + eval ${option_var}+=\"${arguments[$i]}\" + done + else + # END LEGACY MODE + # Call parse_arg and pass the modified list of args as an array of arguments. + parse_arg "${arguments[@]}" + fi + fi + set -x +} + +#================================================= + +# Start or restart a service and follow its booting +# +# usage: ynh_check_starting "Line to match" [Log file] [Timeout] [Service name] +# +# | arg: -m, --line_to_match= - Line to match - The line to find in the log to attest the service have finished to boot. +# | arg: -l, --app_log= - Log file - The log file to watch; specify "systemd" to read systemd journal for specified service +# /var/log/$app/$app.log will be used if no other log is defined. +# | arg: -t, --timeout= - Timeout - The maximum time to wait before ending the watching. Defaut 300 seconds. +# | arg: -n, --service_name= - Service name + +ynh_check_starting () { + # Declare an array to define the options of this helper. + declare -Ar args_array=( [m]=line_to_match= [l]=app_log= [t]=timeout= [n]=service_name= ) + local line_to_match + local app_log + local timeout + local service_name + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + local app_log="${app_log:-/var/log/$service_name/$service_name.log}" + local timeout=${timeout:-300} + local service_name="${service_name:-$app}" + + echo "Starting of $service_name" >&2 + systemctl stop $service_name + local templog="$(mktemp)" + # Following the starting of the app in its log + if [ "$app_log" == "systemd" ] ; then + # Read the systemd journal + journalctl -u $service_name -f --since=-45 > "$templog" & + else + # Read the specified log file + tail -F -n0 "$app_log" > "$templog" & + fi + # Get the PID of the last command + local pid_tail=$! + systemctl start $service_name + + local i=0 + for i in `seq 1 $timeout` + do + # Read the log until the sentence is found, which means the app finished starting. Or run until the timeout. + if grep --quiet "$line_to_match" "$templog" + then + echo "The service $service_name has correctly started." >&2 + break + fi + echo -n "." >&2 + sleep 1 + done + if [ $i -eq $timeout ] + then + echo "The service $service_name didn't fully start before the timeout." >&2 + fi + + echo "" + ynh_clean_check_starting +} + +# Clean temporary process and file used by ynh_check_starting +# (usually used in ynh_clean_setup scripts) +# +# usage: ynh_clean_check_starting +ynh_clean_check_starting () { + # Stop the execution of tail. + kill -s 15 $pid_tail 2>&1 + #echo "$(cat $templog)" + ynh_secure_remove "$templog" 2>&1 +} + +# Reload (or other actions) a service and print a log in case of failure. +# +# usage: ynh_system_reload service_name [action] +# | arg: -n, --service_name= - Name of the service to reload +# | arg: -a, --action= - Action to perform with systemctl. Default: reload +ynh_system_reload () { + # Declare an array to define the options of this helper. + declare -Ar args_array=( [n]=service_name= [a]=action= ) + local service_name + local action + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + local action=${action:-reload} + + # Reload, restart or start and print the log if the service fail to start or reload + systemctl $action $service_name || ( journalctl --lines=20 -u $service_name >&2 && false) +} + +# Execute a command and redirect stdout and stderr in /dev/null +# +# usage: ynh_exec_fully_quiet command to execute +# usage: ynh_exec_fully_quiet "command to execute | following command" +# In case of use of pipes, you have to use double quotes. Otherwise, this helper will be executed with the first command, then be send to the next pipe. +# +# | arg: command - command to execute +ynh_exec_fully_quiet () { + eval $@ > /dev/null 2>&1 +} + +# Send an email to inform the administrator +# +# usage: ynh_send_readme_to_admin app_message [recipients] +# | arg: -m --app_message= - The message to send to the administrator. +# | arg: -r, --recipients= - The recipients of this email. Use spaces to separate multiples recipients. - default: root +# example: "root admin@domain" +# If you give the name of a YunoHost user, ynh_send_readme_to_admin will find its email adress for you +# example: "root admin@domain user1 user2" +ynh_send_readme_to_admin() { + # Declare an array to define the options of this helper. + declare -Ar args_array=( [m]=app_message= [r]=recipients= ) + local app_message + local recipients + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + local app_message="${app_message:-...No specific information...}" + local recipients="${recipients:-root}" + + # Retrieve the email of users + find_mails () { + local list_mails="$1" + local mail + local recipients=" " + # Read each mail in argument + for mail in $list_mails + do + # Keep root or a real email address as it is + if [ "$mail" = "root" ] || echo "$mail" | grep --quiet "@" + then + recipients="$recipients $mail" + else + # But replace an user name without a domain after by its email + if mail=$(ynh_user_get_info "$mail" "mail" 2> /dev/null) + then + recipients="$recipients $mail" + fi + fi + done + echo "$recipients" + } + recipients=$(find_mails "$recipients") + + local mail_subject="☁️🆈🅽🅷☁️: \`$app\` was just installed!" + + local mail_message="This is an automated message from your beloved YunoHost server. +Specific information for the application $app. +$app_message +--- +Automatic diagnosis data from YunoHost +$(yunohost tools diagnosis | grep -B 100 "services:" | sed '/services:/d')" + + # Define binary to use for mail command + if [ -e /usr/bin/bsd-mailx ] + then + local mail_bin=/usr/bin/bsd-mailx + else + local mail_bin=/usr/bin/mail.mailutils + fi + + # Send the email to the recipients + echo "$mail_message" | $mail_bin -a "Content-Type: text/plain; charset=UTF-8" -s "$mail_subject" "$recipients" +} diff --git a/scripts/backup b/scripts/backup new file mode 100644 index 0000000..cd703d4 --- /dev/null +++ b/scripts/backup @@ -0,0 +1,24 @@ +#!/bin/bash +# to test the functionnality : +# yunohost backup create -n "octoprint-test" --apps octoprint +# yunohost backup delete octoprint-test + +source /usr/share/yunohost/helpers + +# manage script failure +ynh_abort_if_errors + +# retrieve arguments +app=$YNH_APP_INSTANCE_NAME +domain=$(ynh_app_setting_get "$app" domain) + +# definie useful vars +final_path="/opt/yunohost/$app" +home_path="/home/$app" + +# backup source & conf files +ynh_backup "$final_path" +ynh_backup "$home_path" +ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" +ynh_backup "/etc/sudoers.d/$app" +ynh_backup "/etc/systemd/system/$app@$app.service" diff --git a/scripts/install b/scripts/install new file mode 100644 index 0000000..f9d32b2 --- /dev/null +++ b/scripts/install @@ -0,0 +1,91 @@ +#!/bin/bash + +source _common.sh +source /usr/share/yunohost/helpers + +# manage script failure +ynh_abort_if_errors + +# retrieve arguments +app=$YNH_APP_INSTANCE_NAME +domain=$YNH_APP_ARG_DOMAIN +is_public=$YNH_APP_ARG_IS_PUBLIC + +# definie useful vars +final_path="/opt/yunohost/$app" +home_path="/home/$app" +data_path="/home/$app/.$app" + +# check domain/path availability +ynh_script_progression --message="Validating installation parameters..." +path_url=$(ynh_normalize_url_path "/") +ynh_webpath_available "$domain" "$path_url" || ynh_die "$domain/$path_url is not available, please use an other domain." +ynh_webpath_register $app "$domain" "$path_url" + +# add required packages +ynh_script_progression --message="Installing dependencies..." +ynh_install_app_dependencies "$PKG_DEPENDENCIES" + +# save app settings +ynh_script_progression --message="Storing installation settings..." +ynh_app_setting_set $app domain "$domain" +ynh_app_setting_set $app is_public $is_public + +# find a free port & open it +ynh_script_progression --message="Looking for a free port and opening it..." +port=$(ynh_find_port 5000) +ynh_app_setting_set $app port $port +ynh_exec_fully_quiet yunohost firewall allow TCP $port + +# create a dedicated system user +ynh_script_progression --message="Creating dedicated user, rights and folders..." +ynh_system_user_create $app +## grant sudo permissions to the user to manage his own systemd service +myynh_create_dir "/etc/sudoers.d" +cp "../conf/sudoers" "/etc/sudoers.d/$app" +## create a directory for the installation of Octoprint +myynh_create_dir "$final_path" +chown $app: "$final_path" +## create a directory for the datas of Octoprint +myynh_create_dir "$data_path" +chown -R $app: "$home_path" + +# installation in a virtual environment +ynh_script_progression --message="Installing Octoprint in a virtual environment..." +exec_as $app -H -s /bin/bash -c " \ + echo 'create the virtual environment' \ + && python3 -m venv "$final_path" \ + && echo 'activate the virtual environment' \ + && source "$final_path/bin/activate" \ + && echo 'install a required python package' \ + && pip install --upgrade wheel \ + && echo 'install Octoprint' \ + && pip install --upgrade $app \ + " + +# set default configuration files +ynh_script_progression --message="Configuring the installation..." +chown -R $app: "$data_path" + +# setup up autostart using systemd +ynh_script_progression --message="Adding the dedicated service..." +ynh_add_systemd_config "$app@$app" +## add service in admin panel +yunohost service add "$app@$app" --log "$data_path/octoprint.log" --description "Octoprint server" + +# enable & restart systemd service +ynh_script_progression --message="Starting the Octoprint server..." +ynh_system_reload --service_name="$app@$app" --action=enable +ynh_check_starting --line_to_match="Octoprint initialized" --app_log="systemd" --timeout=1000 --service_name="$app@$app" + +# create a dedicated nginx config +ynh_script_progression --message="Configuring nginx web server..." +ynh_add_nginx_config +## reload nginx +ynh_system_reload --service_name=nginx + +# unprotect app access if public +ynh_script_progression --message="Configuring SSOwat..." +[ $is_public -eq 1 ] && ynh_app_setting_set $app unprotected_uris "/" + +ynh_script_progression --message="Installation of $app completed" --last diff --git a/scripts/remove b/scripts/remove new file mode 100644 index 0000000..30a4fda --- /dev/null +++ b/scripts/remove @@ -0,0 +1,47 @@ +#!/bin/bash +# to test the functionnality : +# yunohost app remove octoprint + +source _common.sh +source /usr/share/yunohost/helpers + +# retrieve arguments +app=$YNH_APP_INSTANCE_NAME +domain=$(ynh_app_setting_get "$app" domain) +port=$(ynh_app_setting_get $app port) + +# definie useful vars +final_path="/opt/yunohost/$app" +home_path="/home/$app" + +# remove metapackage and its dependencies +ynh_remove_app_dependencies + +# remove the app directory securely +ynh_secure_remove "$final_path" + +# remove the dedicated nginx config +ynh_remove_nginx_config + +# remove a directory securely +ynh_secure_remove "$home_path" + +# remove service from admin panel +if yunohost service status | grep -q "$app@$app" +then + yunohost service remove "$app@$app" +fi + +# remove systemd service +ynh_system_reload --service_name="$app@$app" --action=stop +ynh_system_reload --service_name="$app@$app" --action=disable +ynh_secure_remove "/etc/systemd/system/$app.service" + +# close port +ynh_exec_fully_quiet yunohost firewall disallow TCP $port + +# delete a system user +ynh_system_user_delete "$app" + +# remove sudoers file +ynh_secure_remove "/etc/sudoers.d/$app" diff --git a/scripts/restore b/scripts/restore new file mode 100644 index 0000000..a9b2882 --- /dev/null +++ b/scripts/restore @@ -0,0 +1,66 @@ +#!/bin/bash +# to test the functionnality : +# yunohost backup create -n "octoprint-test" --apps octoprint +# yunohost app remove octoprint +# yunohost backup restore "octoprint-test" + +source ../settings/scripts/_common.sh +source /usr/share/yunohost/helpers + +# manage script failure +ynh_abort_if_errors + +# retrieve arguments +app=$YNH_APP_INSTANCE_NAME +domain=$(ynh_app_setting_get "$app" domain) +port=$(ynh_app_setting_get "$app" port) + +# definie useful vars +final_path="/opt/yunohost/$app" +home_path="/home/$app" +data_path="/home/$app/.$app" + +# check domain/path availability +path_url=$(ynh_normalize_url_path "/") +ynh_webpath_available $domain $path_url || ynh_die "$domain/$path_url is not available, please use an other domain." + +# add required packages +ynh_install_app_dependencies "$PKG_DEPENDENCIES" + +# restore dedicated system user +ynh_system_user_exists "$app" && ynh_die "User $app is not available" +ynh_system_user_create "$app" + +# restore conf files +ynh_restore_file "/etc/nginx/conf.d/$domain.d/$app.conf" +ynh_restore_file "/etc/sudoers.d/$app" +ynh_restore_file "/etc/systemd/system/$app@$app.service" + +# restore source +if [ ! -d "$final_path" ]; then + ynh_restore_file "$final_path" +else + ynh_die "There is already a directory: $final_path" +fi + +# restore data +if [ ! -d "$home_path" ]; then + ynh_restore_file "$home_path" + chown -R $app: "$home_path" +else + ynh_die "$home_path already exists and will not be overwritten" +fi + +# restore port +[ $port -eq $(ynh_find_port $port) ] || ynh_die "$port is not available, please use an other port" +ynh_exec_fully_quiet yunohost firewall allow TCP $port + +# add service in admin panel +yunohost service add "$app@$app" --log "$data_path/octoprint.log" --description "Octoprint server" + +# enable & restart systemd service +ynh_system_reload --service_name="$app@$app" --action=enable +ynh_check_starting --line_to_match="Octoprint initialized" --app_log="systemd" --timeout=1000 --service_name="$app@$app" + +# reload nginx +ynh_system_reload --service_name=nginx diff --git a/scripts/upgrade b/scripts/upgrade new file mode 100644 index 0000000..b1396ca --- /dev/null +++ b/scripts/upgrade @@ -0,0 +1,60 @@ +#!/bin/bash + +source _common.sh +source /usr/share/yunohost/helpers + +# manage script failure +ynh_abort_if_errors + +# retrieve arguments +app=$YNH_APP_INSTANCE_NAME +domain=$(ynh_app_setting_get $app domain) +port=$(ynh_app_setting_get $app port) +is_public=$(ynh_app_setting_get $app is_public) + +# definie useful vars +final_path="/opt/yunohost/$app" + +# use prior backup and restore on error only if backup feature exists on installed instance +if [ -f "/etc/yunohost/apps/$app/scripts/backup" ] ; then + ynh_backup_before_upgrade # Backup the current version of the app + ynh_clean_setup () { + ynh_restore_upgradebackup + } +fi + +# grant sudo permissions to the user to manage his own systemd service +myynh_create_dir "/etc/sudoers.d" +cp "../conf/sudoers" "/etc/sudoers.d/$app" + +# add required packages +ynh_install_app_dependencies "$PKG_DEPENDENCIES" + +# stop systemd service +ynh_system_reload --service_name="$app@$app" --action=stop +ynh_system_reload --service_name="$app@$app" --action=disable + +# upgrade +exec_as "$app" -H -s /bin/bash -c " \ + echo 'create the virtual environment' \ + && python3 -m venv $final_path \ + && echo 'activate the virtual environment' \ + && source $final_path/bin/activate \ + && echo 'install a required python package' \ + && pip install --upgrade wheel \ + && echo 'install Octoprint' \ + && pip install --upgrade $app==0.96.5 \ + " + +# setup up autostart using systemd +ynh_add_systemd_config "$app@$app" + +# enable & restart systemd service +ynh_system_reload --service_name="$app@$app" --action=enable +ynh_check_starting --line_to_match="Octoprint initialized" --app_log="systemd" --timeout=1000 --service_name="$app@$app" + +# create a dedicated nginx config +ynh_add_nginx_config + +# reload nginx +ynh_system_reload --service_name=nginx