diff --git a/src/templates.cpp b/src/templates.cpp index 2cc97f4..18dc304 100644 --- a/src/templates.cpp +++ b/src/templates.cpp @@ -148,9 +148,9 @@ std::string search_string = "TEMPLATE="; std::string replacement_line = "TEMPLATE=" + template_name; // replace the line in the example/service.env file with the replacement line - std::string service_env_path = new_template_path + "/example/service.env"; + std::string service_env_path = new_template_path + "/example/.template_info.env"; if (!replace_line_in_file(service_env_path, search_string, replacement_line)) { - std::cerr << "Error: Failed to replace TEMPLATE= line in service.env file" << std::endl; + std::cerr << "Error: Failed to replace TEMPLATE= line in the .template_info.env file" << std::endl; return; } diff --git a/templates/caddy/README.txt b/templates/caddy/README.txt new file mode 100644 index 0000000..43c0a65 --- /dev/null +++ b/templates/caddy/README.txt @@ -0,0 +1,3 @@ +Caddy! + +Edit the static site, and the Caddyfile. diff --git a/templates/caddy/_common.sh b/templates/caddy/_common.sh new file mode 100644 index 0000000..8c9d4e1 --- /dev/null +++ b/templates/caddy/_common.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +# COMMON FUNCTIONS +# JDE +# 2025-04-25 + +# This file is not required if you write your own template. + + +# Print error message and exit with code 1 +# Usage: die "error message" +die() { + echo -e "\033[91mError: $1\033[0m" + exit 1 +} + +grey_start() { + echo -e -n "\033[90m" +} + +grey_end() { + echo -e -n "\033[0m" +} + +create_and_start_container() { + if [ -z "$1" ] || [ -z "$2" ]; then + die "Template error: create_and_start_container " + fi + + local run_cmd="$1" + local container_name="$2" + + if _is_container_exists $container_name; then + _is_container_running $container_name && return 0 + _start_container $container_name + else + grey_start + $run_cmd + grey_end + fi + + if ! _is_container_running $container_name; then + die "Container ${container_name} failed to start" + fi + + ID=$(_get_container_id $container_name) + echo "Container ${container_name} is running with ID ${ID}" +} + +function create_folder() { + local folder="$1" + if [ -d "$folder" ]; then + return 0 + fi + if ! mkdir -p "$folder"; then + die "Failed to create folder: $folder" + fi + chmod 777 "$folder" + echo "Folder created: $folder" +} + +# Check if docker is installed +_check_docker_installed() { + if ! command -v docker &> /dev/null; then + echo "Docker is not installed" + return 1 + fi + + # check if docker daemon is running + if ! docker info &> /dev/null; then + echo "Docker daemon is not running" + return 1 + fi + + # check if user has permission to run docker + if ! docker run --rm hello-world &> /dev/null; then + echo "User does not have permission to run docker" + return 1 + fi + + return 0 +} + +# Check if a container exists +_is_container_exists() { + if ! docker ps -a --format "{{.Names}}" | grep -q "^$1$"; then + return 1 + fi + return 0 +} + +# Check if a container is running +_is_container_running() { + if ! docker ps --format "{{.Names}}" | grep -q "^$1$"; then + return 1 + fi + return 0 +} + +# get contianer ID +_get_container_id() { + docker ps --format "{{.ID}}" --filter "name=$1" +} + +# get container status +_get_container_status() { + docker ps --format "{{.Status}}" --filter "name=$1" +} + +# start container that exists +_start_container() { + _is_container_exists $1 || return 1 + _is_container_running $1 && return 0 + docker start $1 +} + +# stop container that exists +_stop_container() { + _is_container_running $1 || return 0; + docker stop $1 +} + +# remove container that exists +_remove_container() { + _stop_container $1 + _is_container_exists $1 || return 0; + docker rm $1 +} + +# get container logs +_get_container_logs() { + if ! _is_container_exists $1; then + echo "Container $1 does not exist" + return 1 + fi + + docker logs $1 +} + +check_required_env_vars() { + local required_vars=("$@") + for var in "${required_vars[@]}"; do + if [ -z "${!var}" ]; then + die "Required environment variable $var is not set in your service.env file" + fi + done +} + +function _root_remove_tree() { + local to_remove="$1" + parent=$(dirname "$to_remove") + abs_parent=$(realpath "$parent") + child=$(basename "$to_remove") + docker run --rm -v "$abs_parent":/data alpine rm -rf "/data/$child" +} diff --git a/templates/caddy/_default.env b/templates/caddy/_default.env new file mode 100644 index 0000000..1473fca --- /dev/null +++ b/templates/caddy/_default.env @@ -0,0 +1,8 @@ +# Service settings specific to this server + +# Image settings +IMAGE_REGISTRY="docker.io" +IMAGE_REPO="caddy" + +DATA_VOLUME=caddy_data +CONFIG_VOLUME=caddy_config diff --git a/templates/caddy/backup.sh b/templates/caddy/backup.sh new file mode 100644 index 0000000..deed39e --- /dev/null +++ b/templates/caddy/backup.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# BACKUP SCRIPT +# The backup script is OPTIONAL. +# It is used to backup the service on the server. +# It is called with one argument: the path to the destination backup file. +# If the backup file already exists, the script should exit with a message. + +source "$(dirname "$0")/_common.sh" +check_required_env_vars "CONTAINER_NAME" "DATA_VOLUME" "CONFIG_VOLUME" "CONFIG_PATH" + +# Stop container before backup +_stop_container "$CONTAINER_NAME" + +if ! autobackup volume=$DATA_VOLUME volume=$CONFIG_VOLUME $1 $2; then + die "Failed to create backup" +fi + +# Start container after backup +_start_container "$CONTAINER_NAME" + +echo "Backup created successfully: $BACKUP_FILE" diff --git a/templates/caddy/example/.template_info.env b/templates/caddy/example/.template_info.env new file mode 100644 index 0000000..ee61c54 --- /dev/null +++ b/templates/caddy/example/.template_info.env @@ -0,0 +1,2 @@ +# Template to use - always required! +TEMPLATE=caddy diff --git a/templates/caddy/example/Caddyfile b/templates/caddy/example/Caddyfile new file mode 100644 index 0000000..75fb223 --- /dev/null +++ b/templates/caddy/example/Caddyfile @@ -0,0 +1,2 @@ +# See https://caddyserver.com/docs/caddyfile + diff --git a/templates/caddy/example/service.env b/templates/caddy/example/service.env new file mode 100644 index 0000000..c4f3976 --- /dev/null +++ b/templates/caddy/example/service.env @@ -0,0 +1,10 @@ +# Service settings specific to this server +# (can also override anything in the _default.env file in the template to make it specific to this server) +CONTAINER_NAME=caddy +IMAGE_TAG="latest" + +# Scripts will have these environment variables set, plus those in _default.env, plus: +# SERVER, SERVICE, CONFIG_PATH +# CONFIG_PATH points to this directory! + + diff --git a/templates/caddy/example/static/index.html b/templates/caddy/example/static/index.html new file mode 100644 index 0000000..cd8f430 --- /dev/null +++ b/templates/caddy/example/static/index.html @@ -0,0 +1,9 @@ + + + Static Site + + +

Static Site

+

This is a static site.

+ + \ No newline at end of file diff --git a/templates/caddy/install.sh b/templates/caddy/install.sh new file mode 100644 index 0000000..35dc41a --- /dev/null +++ b/templates/caddy/install.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# INSTALL SCRIPT +# The install script is required for all templates. +# It is used to install the service on the server. +# It is called with the path to the server specific env file as an argument. + +source "$(dirname "$0")/_common.sh" + +# Required environment variables +check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" "DATA_VOLUME" "CONFIG_VOLUME" + +if ! autocreate volume=$DATA_VOLUME volume=$CONFIG_VOLUME; then + die "Failed to autocreate volumes and paths" +fi + +# Test Docker +_check_docker_installed || die "Docker test failed, aborting installation..." + +# check can pull image on remote host and exit if fails +docker pull "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || die "Failed to pull image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" + +[ -f "${CONFIG_PATH}/Caddyfile" ] || die "Caddyfile not found in ${CONFIG_PATH}!" + +# remove and restart, as the env may have changed. +bash ./stop.sh || die "Failed to stop container ${CONTAINER_NAME}" +_remove_container $CONTAINER_NAME || die "Failed to remove container ${CONTAINER_NAME}" +bash ./start.sh || die "Failed to start container ${CONTAINER_NAME}" + +echo "Installation of ${CONTAINER_NAME} complete" diff --git a/templates/caddy/logs.sh b/templates/caddy/logs.sh new file mode 100644 index 0000000..209b57b --- /dev/null +++ b/templates/caddy/logs.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# LOGS SCRIPT +# The logs script is OPTIONAL. +# It is used to return the logs of the service. +# It is called with the path to the server specific env file as an argument. + +source "$(dirname "$0")/_common.sh" + +# Required environment variables +check_required_env_vars "CONTAINER_NAME" + +echo "Container ${CONTAINER_NAME} logs:" +grey_start +docker logs "${CONTAINER_NAME}" +grey_end diff --git a/templates/caddy/nuke.sh b/templates/caddy/nuke.sh new file mode 100644 index 0000000..7d5d717 --- /dev/null +++ b/templates/caddy/nuke.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# NUKE SCRIPT +# This is run after the uninstall.sh script to delete all data. +# dropshell handles the configuration files, so we just need to remove +# any docker volumes and any custom local data folders. + +source "$(dirname "$0")/_common.sh" +check_required_env_vars "CONTAINER_NAME" "DATA_VOLUME" "CONFIG_VOLUME" "CONFIG_PATH" + +if ! autonuke volume=$DATA_VOLUME volume=$CONFIG_VOLUME; then + die "Failed to nuke" +fi + +echo "Nuking of ${CONTAINER_NAME} complete." diff --git a/templates/caddy/ports.sh b/templates/caddy/ports.sh new file mode 100644 index 0000000..1611654 --- /dev/null +++ b/templates/caddy/ports.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +source "$(dirname "$0")/_common.sh" +echo 80 +echo 443 diff --git a/templates/caddy/restore.sh b/templates/caddy/restore.sh new file mode 100644 index 0000000..e58bfb0 --- /dev/null +++ b/templates/caddy/restore.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# RESTORE SCRIPT +# The restore script is OPTIONAL. +# It is used to restore the service on the server from a backup file. +# It is called with one argument: the path to the backup file. + +source "$(dirname "$0")/_common.sh" +check_required_env_vars "DATA_VOLUME" "CONFIG_VOLUME" "CONFIG_PATH" + +# uninstall container before restore +bash ./uninstall.sh || die "Failed to uninstall service before restore" + +# restore data from backup file +if ! autorestore volume=$DATA_VOLUME volume=$CONFIG_VOLUME "$1" "$2"; then + die "Failed to restore data from backup file" +fi + +# reinstall service +bash ./install.sh || die "Failed to reinstall service after restore" + +echo "Restore complete! Service is running again." diff --git a/templates/caddy/start.sh b/templates/caddy/start.sh new file mode 100644 index 0000000..510abff --- /dev/null +++ b/templates/caddy/start.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# START SCRIPT +# The start script is required for all templates. +# It is used to start the service on the server. +# It is called with the path to the server specific env file as an argument. + +source "$(dirname "$0")/_common.sh" +check_required_env_vars "CONTAINER_NAME" "DATA_VOLUME" "CONFIG_VOLUME" + + +DOCKER_RUN_CMD="docker run -d \ + --restart unless-stopped \ + --name ${CONTAINER_NAME} \ + -p 80:80 \ + -p 443:443 \ + -p 443:443/udp \ + -v ${CONFIG_PATH}/Caddyfile:/etc/caddy/Caddyfile \ + -v ${DATA_VOLUME}:/data \ + -v ${CONFIG_VOLUME}:/config \ + -v ${CONFIG_PATH}/static:/srv \ + ${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG}" + + +if ! create_and_start_container "$DOCKER_RUN_CMD" "$CONTAINER_NAME"; then + die "Failed to start container ${CONTAINER_NAME}" +fi + +# Check if the container is running +if ! _is_container_running "$CONTAINER_NAME"; then + die "Container ${CONTAINER_NAME} is not running" +fi + +echo "Container ${CONTAINER_NAME} started" diff --git a/templates/caddy/status.sh b/templates/caddy/status.sh new file mode 100644 index 0000000..afa2d57 --- /dev/null +++ b/templates/caddy/status.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# STATUS SCRIPT +# The status script is OPTIONAL. +# It is used to return the status of the service (0 is healthy, 1 is unhealthy). +# It is called with the path to the server specific env file as an argument. + + +# This is an example of a status script that checks if the service is running. +source "$(dirname "$0")/_common.sh" +check_required_env_vars "CONTAINER_NAME" + +# check if the service is running +_is_container_running $CONTAINER_NAME || die "Service is not running - did not find container $CONTAINER_NAME." + +echo "Service is healthy" +exit 0 diff --git a/templates/caddy/stop.sh b/templates/caddy/stop.sh new file mode 100644 index 0000000..e1cc8e9 --- /dev/null +++ b/templates/caddy/stop.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# STOP SCRIPT +# The stop script is required for all templates. +# It is used to stop the service on the server. +# It is called with the path to the server specific env file as an argument. + +source "$(dirname "$0")/_common.sh" +check_required_env_vars "CONTAINER_NAME" + +_stop_container $CONTAINER_NAME || die "Failed to stop container ${CONTAINER_NAME}" + +echo "Container ${CONTAINER_NAME} stopped" diff --git a/templates/caddy/uninstall.sh b/templates/caddy/uninstall.sh new file mode 100644 index 0000000..094787c --- /dev/null +++ b/templates/caddy/uninstall.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# UNINSTALL SCRIPT +# The uninstall script is required for all templates. +# It is used to uninstall the service from the server. +# It is called with the path to the server specific env file as an argument. + +source "$(dirname "$0")/_common.sh" +check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" + +_remove_container $CONTAINER_NAME || die "Failed to remove container ${CONTAINER_NAME}" +_is_container_running && die "Couldn't stop existing container" +_is_container_exists && die "Couldn't remove existing container" + +# remove the image +docker rmi "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || echo "Failed to remove image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" + +echo "Uninstallation of ${CONTAINER_NAME} complete." +echo "Local data still in place." diff --git a/templates/dropshell-agent/_common.sh b/templates/dropshell-agent/_common.sh new file mode 100644 index 0000000..5bd773b --- /dev/null +++ b/templates/dropshell-agent/_common.sh @@ -0,0 +1,174 @@ +# COMMON FUNCTIONS +# JDE +# 2025-05-03 + +# This file is available TO ***ALL*** templates, as ${AGENT_PATH}/_common.sh + +# ---------------------------------------------------------------------------------------------------------- + +# summary of functions: +# die "message" : Prints an error message in red and exits with status code 1. +# grey_start : Switches terminal output color to grey. +# grey_end : Resets terminal output color from grey. +# create_and_start_container "" : Creates/starts a container, verifying it runs. +# create_folder : Creates a directory if it doesn't exist (chmod 777). +# _check_docker_installed : Checks if Docker is installed, running, and user has permission. Returns 1 on failure. +# _is_container_exists : Checks if a container (any state) exists. Returns 1 if not found. +# _is_container_running : Checks if a container is currently running. Returns 1 if not running. +# _get_container_id : Prints the ID of the named container. +# _get_container_status : Prints the status string of the named container. +# _start_container : Starts an existing, stopped container. +# _stop_container : Stops a running container. +# _remove_container : Stops (if needed) and removes a container. +# _get_container_logs : Prints the logs for a container. +# check_required_env_vars "VAR1" ... : Checks if listed environment variables are set; calls die() if any are missing. +# _root_remove_tree : Removes a path using a root Docker container (for permissions). + +# ---------------------------------------------------------------------------------------------------------- + +# Print error message and exit with code 1 +# Usage: die "error message" +die() { + echo -e "\033[91mError: $1\033[0m" + exit 1 +} + +grey_start() { + echo -e -n "\033[90m" +} + +grey_end() { + echo -e -n "\033[0m" +} + +create_and_start_container() { + if [ -z "$1" ] || [ -z "$2" ]; then + die "Template error: create_and_start_container " + fi + + local run_cmd="$1" + local container_name="$2" + + if _is_container_exists $container_name; then + _is_container_running $container_name && return 0 + _start_container $container_name + else + grey_start + $run_cmd + grey_end + fi + + if ! _is_container_running $container_name; then + die "Container ${container_name} failed to start" + fi + + ID=$(_get_container_id $container_name) + echo "Container ${container_name} is running with ID ${ID}" +} + +function create_folder() { + local folder="$1" + if [ -d "$folder" ]; then + return 0 + fi + if ! mkdir -p "$folder"; then + die "Failed to create folder: $folder" + fi + chmod 777 "$folder" + echo "Folder created: $folder" +} + +# Check if docker is installed +_check_docker_installed() { + if ! command -v docker &> /dev/null; then + echo "Docker is not installed" + return 1 + fi + + # check if docker daemon is running + if ! docker info &> /dev/null; then + echo "Docker daemon is not running" + return 1 + fi + + # check if user has permission to run docker + if ! docker run --rm hello-world &> /dev/null; then + echo "User does not have permission to run docker" + return 1 + fi + + return 0 +} + +# Check if a container exists +_is_container_exists() { + if ! docker ps -a --format "{{.Names}}" | grep -q "^$1$"; then + return 1 + fi + return 0 +} + +# Check if a container is running +_is_container_running() { + if ! docker ps --format "{{.Names}}" | grep -q "^$1$"; then + return 1 + fi + return 0 +} + +# get contianer ID +_get_container_id() { + docker ps --format "{{.ID}}" --filter "name=$1" +} + +# get container status +_get_container_status() { + docker ps --format "{{.Status}}" --filter "name=$1" +} + +# start container that exists +_start_container() { + _is_container_exists $1 || return 1 + _is_container_running $1 && return 0 + docker start $1 +} + +# stop container that exists +_stop_container() { + _is_container_running $1 || return 0; + docker stop $1 +} + +# remove container that exists +_remove_container() { + _stop_container $1 + _is_container_exists $1 || return 0; + docker rm $1 +} + +# get container logs +_get_container_logs() { + if ! _is_container_exists $1; then + echo "Container $1 does not exist" + return 1 + fi + + docker logs $1 +} + +check_required_env_vars() { + local required_vars=("$@") + for var in "${required_vars[@]}"; do + if [ -z "${!var}" ]; then + die "Required environment variable $var is not set in your service.env file" + fi + done +} + +function _root_remove_tree() { + local to_remove="$1" + parent=$(dirname "$to_remove") + abs_parent=$(realpath "$parent") + child=$(basename "$to_remove") + docker run --rm -v "$abs_parent":/data alpine rm -rf "/data/$child" +} diff --git a/templates/example-nginx/README.txt b/templates/example-nginx/README.txt index b1da890..4cbd8ba 100644 --- a/templates/example-nginx/README.txt +++ b/templates/example-nginx/README.txt @@ -1,40 +1 @@ -DropShell Template Example - Nginx simple webserver - -A simple service example, creating a single docker container running nginx, with contant on the host -in the configurable LOCAL_DATA_FOLDER directory. - -Shell scripts defined in this folder are run as DropShell commands on the remote server (not locally!). - -When they are run, the following environment variables will be set: -- SERVER (server name), SERVICE (service name) and CONFIG_PATH (path to the user's service configuration folder) -- everything in _default.env -- everything in the server's particular service.env file (defaults in example/service.env) - -The optional backup and restore scripts get two additional arguments, which are the backup file to create/restore -(must be a single tgz file), and an empty temporary directory on the server which automatically gets cleaned up. - -Mandatory scripts are: -- install.sh (installs, or if already installed updates - non-destructive) -- uninstall.sh (preserves data) -- nuke.sh (deletes all data) - -Optional standard scripts are: -- start.sh -- stop.sh -- backup.sh -- restore.sh -- status.sh -- ports.sh -- logs.sh - - -The example/ folder gets copied to the service's configuration, edited for the particular server settings, -then copied onto the server. The location is server-specific, but can be accessed via CONFIG_PATH. - -You can use it to store things like an nginx config file (this simple example does not). - -The example/.template_info.env must contain the TEMPLATE= definition, which must match the template folder name -(in this case 'example-nginx'). - -You can start a new template with the command: -dropshell create-template +Caddy diff --git a/templates/squashkiwi/_common.sh b/templates/squashkiwi/_common.sh new file mode 100755 index 0000000..386ddcb --- /dev/null +++ b/templates/squashkiwi/_common.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +# COMMON FUNCTIONS +# JDE +# 2025-04-25 + +# This file is not required if you write your own template. + +# Print error message and exit with code 1 +# Usage: die "error message" +die() { + echo -e "\033[91mError: $1\033[0m" + exit 1 +} + +grey_start() { + echo -e -n "\033[90m" +} + +grey_end() { + echo -e -n "\033[0m" +} + +create_and_start_container() { + if [ -z "$1" ] || [ -z "$2" ]; then + die "Template error: create_and_start_container " + fi + + local run_cmd="$1" + local container_name="$2" + + if _is_container_exists $container_name; then + _is_container_running $container_name && return 0 + _start_container $container_name + else + echo "Creating and running $container_name" + grey_start + $run_cmd + grey_end + fi + + if ! _is_container_running $container_name; then + die "Container ${container_name} failed to start" + fi + + ID=$(_get_container_id $container_name) + echo "Container ${container_name} is running with ID ${ID}" +} + +function create_folder() { + local folder="$1" + if [ -d "$folder" ]; then + return 0 + fi + if ! mkdir -p "$folder"; then + die "Failed to create folder: $folder" + fi + chmod 777 "$folder" + echo "Folder created: $folder" +} + +# Check if docker is installed +_check_docker_installed() { + if ! command -v docker &> /dev/null; then + echo "Docker is not installed" + return 1 + fi + + # check if docker daemon is running + if ! docker info &> /dev/null; then + echo "Docker daemon is not running" + return 1 + fi + + # check if user has permission to run docker + if ! docker run --rm hello-world &> /dev/null; then + echo "User does not have permission to run docker" + return 1 + fi + + return 0 +} + +# Check if a container exists +_is_container_exists() { + docker ps -a --format "{{.Names}}" | grep -q "^$1$" +} + +# Check if a container is running +_is_container_running() { + docker ps --format "{{.Names}}" | grep -q "^$1$" +} + +# get contianer ID +_get_container_id() { + docker ps --format "{{.ID}}" --filter "name=$1" +} + +# get container status +_get_container_status() { + docker ps --format "{{.Status}}" --filter "name=$1" +} + +# start container that exists +_start_container() { + _is_container_exists $1 || return 1 + _is_container_running $1 && return 0 + docker start $1 +} + +# stop container that exists +_stop_container() { + _is_container_running $1 || return 0; + docker stop $1 +} + +# remove container that exists +_remove_container() { + _stop_container $1 + _is_container_exists $1 || return 0; + docker rm $1 +} + +# get container logs +_get_container_logs() { + if ! _is_container_exists $1; then + echo "Container $1 does not exist" + return 1 + fi + + docker logs $1 +} + +check_required_env_vars() { + local required_vars=("$@") + for var in "${required_vars[@]}"; do + if [ -z "${!var}" ]; then + die "Required environment variable $var is not set in your service.env file" + fi + done +} + + +function _root_remove_tree() { + local to_remove="$1" + parent=$(dirname "$to_remove") + abs_parent=$(realpath "$parent") + child=$(basename "$to_remove") + docker run --rm -v "$abs_parent":/data alpine rm -rf "/data/$child" +} diff --git a/templates/squashkiwi/_default.env b/templates/squashkiwi/_default.env new file mode 100644 index 0000000..d1c0279 --- /dev/null +++ b/templates/squashkiwi/_default.env @@ -0,0 +1,10 @@ +# Application settings +CONTAINER_PORT=8181 + +# Deployment settings +CONTAINER_NAME="squashkiwi" + +# Image settings +IMAGE_REGISTRY="gitea.jde.nz" +IMAGE_REPO="squashkiwi/squashkiwi" +IMAGE_TAG="latest" diff --git a/templates/squashkiwi/backup.sh b/templates/squashkiwi/backup.sh new file mode 100644 index 0000000..c060d97 --- /dev/null +++ b/templates/squashkiwi/backup.sh @@ -0,0 +1,29 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" + +# Get backup file path from argument +BACKUP_FILE="$1" +if [ -z "$BACKUP_FILE" ]; then + die "Backup file path not provided" +fi + +# Check if backup file already exists +if [ -f "$BACKUP_FILE" ]; then + die "Backup file $BACKUP_FILE already exists" +fi + +# Stop container before backup +_stop_container "$CONTAINER_NAME" + +# Create backup of data folder +# We include the parent folder in the backup paths (.), and strip on restore. +echo "Creating backup of $LOCAL_DATA_FOLDER..." +if ! tar zcvf "$BACKUP_FILE" -C "$LOCAL_DATA_FOLDER" .; then + _start_container "$CONTAINER_NAME" + die "Failed to create backup" +fi + +# Start container after backup +_start_container "$CONTAINER_NAME" + +echo "Backup created successfully: $BACKUP_FILE" diff --git a/templates/squashkiwi/example/.template_info.env b/templates/squashkiwi/example/.template_info.env new file mode 100644 index 0000000..ab4665d --- /dev/null +++ b/templates/squashkiwi/example/.template_info.env @@ -0,0 +1,2 @@ +# Template to use - always required! +TEMPLATE=squashkiwi diff --git a/templates/squashkiwi/example/service.env b/templates/squashkiwi/example/service.env new file mode 100644 index 0000000..4e40a47 --- /dev/null +++ b/templates/squashkiwi/example/service.env @@ -0,0 +1,6 @@ +# Service settings specific to this server +# (can also override anything in the _basic.env file in the template to make it specific to this server) + +HOST_PORT=80 +LOCAL_DATA_FOLDER="${HOME}/.sk" +IMAGE_TAG="latest" diff --git a/templates/squashkiwi/install.sh b/templates/squashkiwi/install.sh new file mode 100755 index 0000000..5646333 --- /dev/null +++ b/templates/squashkiwi/install.sh @@ -0,0 +1,36 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" + +check_required_env_vars \ + "IMAGE_REGISTRY" \ + "IMAGE_REPO" \ + "IMAGE_TAG" \ + "CONTAINER_NAME" \ + "LOCAL_DATA_FOLDER" + + +# Create local data folder if it doesn't exist +if [ -d "${LOCAL_DATA_FOLDER}" ]; then + echo "Local data folder ${LOCAL_DATA_FOLDER} exists, using existing data." +else + echo "Local data folder ${LOCAL_DATA_FOLDER} does not exist, creating..." + mkdir -p "${LOCAL_DATA_FOLDER}" +fi + + +# Test Docker +_check_docker_installed || die "Docker test failed, aborting installation..." + +# Create deploy and data folders +[ -z "$LOCAL_DATA_FOLDER" ] && die "LOCAL_DATA_FOLDER is not set" +create_folder "$LOCAL_DATA_FOLDER" + +# check can pull image on remote host and exit if fails +docker pull "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || die "Failed to pull image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" + +# remove and restart, as the env may have changed. +bash ./stop.sh || die "Failed to stop container ${CONTAINER_NAME}" +_remove_container $CONTAINER_NAME || die "Failed to remove container ${CONTAINER_NAME}" +bash ./start.sh || die "Failed to start container ${CONTAINER_NAME}" + +echo "Installation of ${CONTAINER_NAME} complete" diff --git a/templates/squashkiwi/logs.sh b/templates/squashkiwi/logs.sh new file mode 100644 index 0000000..684d64d --- /dev/null +++ b/templates/squashkiwi/logs.sh @@ -0,0 +1,11 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" + +# check required env vars +check_required_env_vars \ + "CONTAINER_NAME" + +echo "Container ${CONTAINER_NAME} logs:" +grey_start +docker logs "${CONTAINER_NAME}" +grey_end diff --git a/templates/squashkiwi/ports.sh b/templates/squashkiwi/ports.sh new file mode 100644 index 0000000..d9792fb --- /dev/null +++ b/templates/squashkiwi/ports.sh @@ -0,0 +1,6 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" + +check_required_env_vars "HOST_PORT" + +echo $HOST_PORT diff --git a/templates/squashkiwi/restore.sh b/templates/squashkiwi/restore.sh new file mode 100644 index 0000000..335938a --- /dev/null +++ b/templates/squashkiwi/restore.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# RESTORE SCRIPT +# The restore script is OPTIONAL. +# It is used to restore the service on the server from a backup file. +# It is called with one argument: the path to the backup file. + +source "$(dirname "$0")/_common.sh" +check_required_env_vars "CONTAINER_NAME" "LOCAL_DATA_FOLDER" + +# Get backup file path from first argument +BACKUP_FILE="$1" +if [ -z "$BACKUP_FILE" ]; then + die "Backup file path not provided" +fi + +# Check if backup file already exists +if [ ! -f "$BACKUP_FILE" ]; then + die "Backup file $BACKUP_FILE does not exist" +fi + +# # Stop container before backup +bash ./uninstall.sh || die "Failed to uninstall service before restore" + +# Remove existing data folder +echo "Deleting ALL data in $LOCAL_DATA_FOLDER." +_root_remove_tree "$LOCAL_DATA_FOLDER" +[ ! -d "$LOCAL_DATA_FOLDER" ] || die "Failed to delete $LOCAL_DATA_FOLDER" +mkdir -p "$LOCAL_DATA_FOLDER" +[ -d "$LOCAL_DATA_FOLDER" ] || die "Failed to create $LOCAL_DATA_FOLDER" + +# Restore data folder from backup +# --strip-components=1 removes the parent folder in the tgz from the restore paths. +if ! tar xzvf "$BACKUP_FILE" -C "$LOCAL_DATA_FOLDER" --strip-components=1; then + die "Failed to restore data folder from backup" +fi + +# reinstall service +bash ./install.sh || die "Failed to reinstall service after restore" + +echo "Restore complete! Service is running again on port $HOST_PORT with restored website." diff --git a/templates/squashkiwi/ssh.sh b/templates/squashkiwi/ssh.sh new file mode 100644 index 0000000..01d620d --- /dev/null +++ b/templates/squashkiwi/ssh.sh @@ -0,0 +1,13 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" + + +if ! _is_container_running "$CONTAINER_NAME"; then + die "Container ${CONTAINER_NAME} is not running. Can't connect to it." +fi + +echo "Connecting to ${CONTAINER_NAME}..." + +docker exec -it ${CONTAINER_NAME} bash + +echo "Disconnected from ${CONTAINER_NAME}" diff --git a/templates/squashkiwi/start.sh b/templates/squashkiwi/start.sh new file mode 100755 index 0000000..d672d7a --- /dev/null +++ b/templates/squashkiwi/start.sh @@ -0,0 +1,31 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" + +check_required_env_vars \ + "CONTAINER_NAME" \ + "HOST_PORT" \ + "CONTAINER_PORT" \ + "LOCAL_DATA_FOLDER" \ + "IMAGE_REGISTRY" \ + "IMAGE_REPO" \ + "IMAGE_TAG" + + +DOCKER_RUN_CMD="docker run -d \ + --restart unless-stopped \ + --name ${CONTAINER_NAME} \ + -p ${HOST_PORT}:${CONTAINER_PORT} \ + -v ${LOCAL_DATA_FOLDER}:/skdata \ + ${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG}" + + +if ! create_and_start_container "$DOCKER_RUN_CMD" "$CONTAINER_NAME"; then + die "Failed to start container ${CONTAINER_NAME}" +fi + +# Check if the container is running +if ! _is_container_running "$CONTAINER_NAME"; then + die "Container ${CONTAINER_NAME} is not running" +fi + +echo "Container ${CONTAINER_NAME} started, on port ${HOST_PORT}" diff --git a/templates/squashkiwi/status.sh b/templates/squashkiwi/status.sh new file mode 100644 index 0000000..c04e447 --- /dev/null +++ b/templates/squashkiwi/status.sh @@ -0,0 +1,16 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" + +check_required_env_vars \ + "CONTAINER_NAME" \ + "HOST_PORT" + +# check if the service is running +_is_container_running $CONTAINER_NAME || die "Service is not running - did not find container $CONTAINER_NAME." + +# check if the service is healthy +curl -s -X GET http://localhost:${HOST_PORT}/health | grep -q "OK" \ + || die "Service is not healthy - did not get OK response from /health endpoint." + +echo "Service is healthy" +exit 0 diff --git a/templates/squashkiwi/stop.sh b/templates/squashkiwi/stop.sh new file mode 100755 index 0000000..442cc13 --- /dev/null +++ b/templates/squashkiwi/stop.sh @@ -0,0 +1,9 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" + +check_required_env_vars \ + "CONTAINER_NAME" + +_stop_container $CONTAINER_NAME || die "Failed to stop container ${CONTAINER_NAME}" + +echo "Container ${CONTAINER_NAME} stopped" diff --git a/templates/squashkiwi/uninstall.sh b/templates/squashkiwi/uninstall.sh new file mode 100644 index 0000000..185a91b --- /dev/null +++ b/templates/squashkiwi/uninstall.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# UNINSTALL SCRIPT +# The uninstall script is required for all templates. +# It is used to uninstall the service from the server. +# It is called with the path to the server specific env file as an argument. + +source "$(dirname "$0")/_common.sh" +check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" + +_remove_container $CONTAINER_NAME || die "Failed to remove container ${CONTAINER_NAME}" +_is_container_running && die "Couldn't stop existing container" +_is_container_exists && die "Couldn't remove existing container" + +# remove the image +docker rmi "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || echo "Failed to remove image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" + +echo "Uninstallation of ${CONTAINER_NAME} complete." +echo "Local data folder ${LOCAL_DATA_FOLDER} still in place."