Switch to using Docker

This commit refactors all code in this project to make use of docker.
This has not yet been documented properly.
This commit is contained in:
Tom Wiesing 2020-06-26 12:54:47 +02:00
parent 9ece280e72
commit 76ef5d8e68
No known key found for this signature in database
GPG key ID: DC1F29F2BC78AB15
43 changed files with 943 additions and 545 deletions

61
distillery/lib/00_init.sh Normal file
View file

@ -0,0 +1,61 @@
#!/bin/bash
set -e
# This is a library file.
# It should be 'source'd only, if it is not we bail out here.
if [[ "$0" = "$BASH_SOURCE" ]]; then
echo "This file should not be executed directly, it should be 'source'd only. "
exit 1;
fi
# Check that we are running on a linux system, to prevent accidentally running on Windows or Mac.
# Ideally we would want to explicitly limit to Debian / Ubuntu but at this point that's not needed.
OS="$(uname -s)"
case "${OS}" in
Linux*) :;;
*) echo "This script must be run under Linux. "; exit 1;;
esac
# To prevent accidentally messing up permissions, we need to always run as root.
# Check that the uid is 0, and otherwise bail out.
if [[ $EUID -ne 0 ]]; then
echo "This script should be run as root, use 'sudo' if in doubt. "
exit 1;
fi
# We enable shell aliases, to allow us to setup utility functions much more easily.
# To be safe that we don't have any other ones in the environment, we first unalias everything.
unalias -a
shopt -s expand_aliases
# Setup some basic input/output functions
function log_info() {
echo -e "\033[1m$1\033[0m"
}
function log_ok() {
echo -e "\033[0;32m$1\033[0m"
}
function log_warn() {
echo -e "\033[1;33m$1\033[0m"
}
function log_error() {
echo -e "\033[0;31m$1\033[0m"
}
if [ -n "$DISABLE_LOG" ]; then
function log_info() {
true
}
function log_ok() {
true
}
function log_warn() {
true
}
function log_error() {
true
}
fi

168
distillery/lib/10_config.sh Normal file
View file

@ -0,0 +1,168 @@
#!/bin/bash
set -e
# This is a library file.
# It should be 'source'd only, if it is not we bail out here.
if [[ "$0" = "$BASH_SOURCE" ]]; then
echo "This file should not be executed directly, it should be 'source'd only. "
exit 1;
fi
# The Path to the configuration file.
CONFIG_FILE="$SCRIPT_DIR/.env"
# Check that the configuration file exists.
# If it does not, throw an error
log_info " => Reading configuration file"
if ! [ -f "$CONFIG_FILE" ]; then
log_error ""
log_error "Missing configuration, provide a '.env' file"
exit 1
fi
# 'source' in the configuration file.
# Ideally we would want to make sure to prevent code-executation within the .env file
# But for the moment let's not.
source "$CONFIG_FILE"
# Next, validate all the configuration settings.
# is_valid_slug checks if it's argument is a valid 'slug'.
# A slug is any non-empty string of alphanumeric characters or '-'s.
# The first character of a slug may not be a dash.
function is_valid_slug() {
if [[ "$1" =~ ^[a-zA-Z0-9][-a-zA-Z0-9]*$ ]]; then
return 0;
else
return 1;
fi;
}
# is_valid_abspath checks if it's argument is an absolute path.
function is_valid_abspath() {
if [[ "$1" =~ ^\/(.+)\/([^/]+)$ ]]; then
return 0;
else
return 1;
fi;
}
# 'is_valid_domain' checks if a number is a valid domain.
# A domain consists of at least one slug, seperated by '.'s.
# Each token is a slug.
function is_valid_domain() {
if [[ "$1" =~ ^([a-zA-Z0-9][-a-zA-Z0-9]*\.)*[a-zA-Z0-9][-a-zA-Z0-9]*$ ]]; then
return 0;
else
return 1;
fi;
}
# 'is_valid_number' checks if a value is a valid number.
function is_valid_number() {
if [[ "$1" =~ ^[1-9][0-9]*$ ]]; then
return 0;
else
return 1;
fi;
}
# 'is_valid_email' checks if a value is a valid email address
function is_valid_email() {
if [[ "$1" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
return 0;
else
return 1;
fi
}
# The 'DRUPAL_ROOT' variable must be an absolute path.
if ! is_valid_abspath "$DRUPAL_ROOT"; then
log_error "Variable 'DRUPAL_ROOT' is missing or not a valid path. ";
log_info "Please verify that it is set correctly in '.env'. ";
log_info "Please ensure that it does not end in '/'. ";
exit 1;
fi
# The 'COMPOSER_ROOT' variable must be an absolute path.
if ! is_valid_abspath "$COMPOSER_ROOT"; then
log_error "Variable 'COMPOSER_ROOT' is missing or not a valid path. ";
log_info "Please verify that it is set correctly in '.env'. ";
log_info "Please ensure that it does not end in '/'. ";
exit 1;
fi
# The 'MYSQL_USER_PREFIX' variable must be a valid slug.
if ! is_valid_slug "$MYSQL_USER_PREFIX"; then
log_error "Variable 'MYSQL_USER_PREFIX' is missing or not a valid slug. ";
log_info "Please verify that it is set correctly in '.env'. ";
exit 1;
fi
# The 'MYSQL_DATABASE_PREFIX' variable must be a valid slug.
if ! is_valid_slug "$MYSQL_DATABASE_PREFIX"; then
log_error "Variable 'MYSQL_DATABASE_PREFIX' is missing or not a valid slug. ";
log_info "Please verify that it is set correctly in '.env'. ";
exit 1;
fi
# The 'DISTILLERY_BOOKKEEPING_DATABASE' variable must be a valid slug.
if ! is_valid_slug "$DISTILLERY_BOOKKEEPING_DATABASE"; then
log_error "Variable 'DISTILLERY_BOOKKEEPING_DATABASE' is missing or not a valid slug. ";
log_info "Please verify that it is set correctly in '.env'. ";
exit 1;
fi
# The 'DISTILLERY_BOOKKEEPING_TABLE' variable must be a valid slug.
if ! is_valid_slug "$DISTILLERY_BOOKKEEPING_TABLE"; then
log_error "Variable 'DISTILLERY_BOOKKEEPING_TABLE' is missing or not a valid slug. ";
log_info "Please verify that it is set correctly in '.env'. ";
exit 1;
fi
# The 'GRAPHDB_USER_PREFIX' variable must be a valid slug.
if ! is_valid_slug "$GRAPHDB_USER_PREFIX"; then
log_error "Variable 'DATABASE_PREFIX' is missing or not a valid slug. ";
log_info "Please verify that it is set correctly in '.env'. ";
exit 1;
fi
# The 'GRAPHDB_REPO_PREFIX' variable must be a valid slug.
if ! is_valid_slug "$GRAPHDB_REPO_PREFIX"; then
log_error "Variable 'GRAPHDB_REPO_PREFIX' is missing or not a valid slug. ";
log_info "Please verify that it is set correctly in '.env'. ";
exit 1;
fi
# The 'DEFAULT_DOMAIN' variable must be a valid domain.
if ! is_valid_domain "$DEFAULT_DOMAIN"; then
log_error "Variable 'DEFAULT_DOMAIN' is missing or not a valid domain. ";
log_info "Please verify that it is set correctly in '.env'. ";
exit 1;
fi
# The 'CERTBOT_EMAIL' variable should either be empty or a valid email
if [ -n "$CERTBOT_EMAIL" ]; then
if ! is_valid_email "$CERTBOT_EMAIL"; then
log_error "Variable 'CERTBOT_EMAIL' is not a valid email address. ";
log_info "Please verify that it is set correctly in '.env' or remove it completly. ";
exit 1;
fi;
fi
# The 'PASSWORD_LENGTH' variable must be a valid number.
if ! is_valid_number "$PASSWORD_LENGTH"; then
log_error "Variable 'PASSWORD_LENGTH' is missing or not a valid number. ";
log_info "Please verify that it is set correctly in '.env'. ";
exit 1;
fi
# paths to composer things
COMPOSER_WEB_DIR="$COMPOSER_ROOT/core/web"
COMPOSER_TRIPLESTORE_DIR="$COMPOSER_ROOT/core/triplestore"
COMPOSER_SQL_DIR="$COMPOSER_ROOT/core/sql"
COMPOSER_INSTANCES_DIR="$COMPOSER_ROOT/instances"
log_ok "Read and validated configuration file. "

86
distillery/lib/20_sql.sh Normal file
View file

@ -0,0 +1,86 @@
#!/bin/bash
set -e
# This is a library file.
# It should be 'source'd only, if it is not we bail out here.
if [[ "$0" = "$BASH_SOURCE" ]]; then
echo "This file should not be executed directly, it should be 'source'd only. "
exit 1;
fi
###
### General SQL functions
###
# wait_for_sql waits for the sql database to be ready
function wait_for_sql() {
log_info "Waiting for database to start ..."
_wait_for_sql_internal
log_ok "Database responded to query "
}
function _wait_for_sql_internal() {
timeout=30
times=1
until dockerized_mysql -e 'show databases;' > /dev/null 2>&1; do
((times=times+1))
if [ "$times" -gt $timeout ]; then
log_error "Database timed out after ${timeout} seconds(s). "
fi;
echo -n "."
sleep 1
done
}
# 'dockerized_mysql' runs an sql command in the sql docker container
function dockerized_mysql() {
pushd "$COMPOSER_SQL_DIR" > /dev/null
docker exec -i `docker-compose ps -q sql` mysql "$@"
retval=$?
popd > /dev/null
return $retval
}
# 'dockerized_mysql' runs an sql command in the sql docker container interactively
function dockerized_mysql_interactive() {
pushd "$COMPOSER_SQL_DIR" > /dev/null
docker exec -ti `docker-compose ps -q sql` mysql "$@"
retval=$?
popd > /dev/null
return $retval
}
###
### Bookkeeping sql
###
# 'sql_bookkeep_exists' checks if a given site already exists
function sql_bookkeep_exists() {
COUNT=$(dockerized_mysql -D "$DISTILLERY_BOOKKEEPING_DATABASE" -e "SELECT COUNT(*) FROM \`$DISTILLERY_BOOKKEEPING_TABLE\` WHERE slug=\"$1\"; " | tail -n +2)
if [ "$COUNT" = "1" ]; then
return 0;
else
return 1;
fi;
}
# 'sql_bookkeep_insert' inserts a new pair of values into the sql database
function sql_bookkeep_insert() {
dockerized_mysql -D "$DISTILLERY_BOOKKEEPING_DATABASE" -e "INSERT INTO \`$DISTILLERY_BOOKKEEPING_TABLE\`($1) VALUES ($2) ;"
}
# 'sql_bookkeep_delete' removes a slug into the bookeeping table
function sql_bookeep_delete() {
dockerized_mysql -D "$DISTILLERY_BOOKKEEPING_DATABASE" -e "DELETE FROM \`$DISTILLERY_BOOKKEEPING_TABLE\` WHERE slug=\"$1\";"
}
# 'sql_bookkeep_load' reads from the bookkeeping table
function sql_bookkeep_load() {
dockerized_mysql -D "$DISTILLERY_BOOKKEEPING_DATABASE" -e "SELECT $2 FROM \`$DISTILLERY_BOOKKEEPING_TABLE\` WHERE slug=\"$1\";"
}
# 'sql_bookkeep_list' lists all slugs from the bookkeeping table
function sql_bookkeep_list() {
dockerized_mysql -D "$DISTILLERY_BOOKKEEPING_DATABASE" -e "SELECT slug FROM \`$DISTILLERY_BOOKKEEPING_TABLE\`; " | tail -n +2
}

67
distillery/lib/30_slug.sh Normal file
View file

@ -0,0 +1,67 @@
#!/bin/bash
set -e
# This is a library file.
# It should be 'source'd only, if it is not we bail out here.
if [[ "$0" = "$BASH_SOURCE" ]]; then
echo "This file should not be executed directly, it should be 'source'd only. "
exit 1;
fi
# This file reads a single slug command line option.
# This is validated when 'require_slug_argument' is called.
function require_slug_argument() {
# The 'SLUG' argument must be a valid slug.
if ! is_valid_slug "$SLUG"; then
log_error "Argument 'SLUG' is missing or not a valid slug. ";
log_info "Please provide it via the command line. ";
exit 1;
fi
log_info " => Deriving configuration for '$SLUG'. "
echo "Domain Name: $INSTANCE_DOMAIN"
echo "Base Directory: $INSTANCE_BASE_DIR"
echo "MySQL User: $MYSQL_USER"
echo "MySQL Database: $MYSQL_DATABASE"
echo "GraphDB User: $GRAPHDB_USER"
echo "GraphDB Repository: $GRAPHDB_REPO"
}
# Read the slug argument.
# We also read it in for scripts where it is not required, and will only use it if that is the case.
SLUG="$1"
# Compute the domain name for this instance.
# Also lowercase the domain name for consistency.
INSTANCE_DOMAIN="$SLUG.$DEFAULT_DOMAIN"
INSTANCE_DOMAIN="$(echo "$INSTANCE_DOMAIN" | tr '[:upper:]' '[:lower:]')"
# Next we need a username base.
# This will be used as a username across the system (linux), MySQL and GraphDB.
# For this we can only allow [0-9a-zA-Z-], hence we have to escape.
# In most cases, the only characters that require escaping are '.'s.
# Hence we replace '.' with '-'s.
# We replace the other two characters that require escaping (_ and -)s with --u and --s respectively.
# Because no two dots can ever follow each other in the INSTANCE_DOMAIN, this is guaranteed collision free.
# We also have to do the '-' replacement first, to prevent escaped other characters from being escaped twice.
USERNAME_BASE="$SLUG"
USERNAME_BASE="${USERNAME_BASE//-/--d}"
USERNAME_BASE="${USERNAME_BASE//_/--u}"
USERNAME_BASE="${USERNAME_BASE//./-}"
# Generate the user and database names for the various systems
MYSQL_USER="${MYSQL_USER_PREFIX}${USERNAME_BASE}"
MYSQL_DATABASE="${MYSQL_DATABASE_PREFIX}${USERNAME_BASE}"
GRAPHDB_USER="${GRAPHDB_USER_PREFIX}${USERNAME_BASE}"
GRAPHDB_REPO="${GRAPHDB_REPO_PREFIX}${USERNAME_BASE}"
# Compute the base directory for the files that will live on disk.
INSTANCE_BASE_DIR="$COMPOSER_INSTANCES_DIR/$INSTANCE_DOMAIN"
INSTANCE_DATA_DIR="$INSTANCE_BASE_DIR/data/"
if [ -n "$CERTBOT_EMAIL" ]; then
LETSENCRYPT_HOST="$INSTANCE_DOMAIN"
LETSENCRYPT_EMAIL="$CERTBOT_EMAIL"
fi;

View file

@ -0,0 +1,49 @@
#!/bin/bash
set -e
# This is a library file.
# It should be 'source'd only, if it is not we bail out here.
if [[ "$0" = "$BASH_SOURCE" ]]; then
echo "This file should not be executed directly, it should be 'source'd only. "
exit 1;
fi
RESOURCE_DIR="$SCRIPT_DIR/resources"
TEMPLATE_DIR="$RESOURCE_DIR/templates/"
# load_template will load a template $1 from the template directory
# and replace ${$2} with $3, ${$4} with $5 etc.
# echoes out the replaced template
function load_template() {
# read the template then remove the argument
TEMPLATE=`cat "$TEMPLATE_DIR/$1"`
shift 1;
# while we have a variable to substiute
while [ ! -z "$1" ]
do
# do the substitution
TEMPLATE="${TEMPLATE//\$\{$1\}/$2}"
shift 2
done;
# finally echo out the template
echo "$TEMPLATE"
}
# install_resource_dir will copy over a resource directory
# to a destination path recursively.
function install_resource_dir() {
from="$RESOURCE_DIR/$1"
to=$2
# copythe template files
for filename in "$from"/*; do
dest="$to/`basename "${filename}"`"
echo "Writing \"$dest\""
cp -r "$filename" "$dest"
done
}
# path where common apache files will be installed.
WISSKI_COMMON_PATH="/etc/apache2/conf/wisski"

View file

@ -0,0 +1,16 @@
#!/bin/bash
set -e
# This is a library file.
# It should be 'source'd only, if it is not we bail out here.
if [[ "$0" = "$BASH_SOURCE" ]]; then
echo "This file should not be executed directly, it should be 'source'd only. "
exit 1;
fi
# Set a few variables to point to the debian frontend
export DEBIAN_FRONTEND=noninteractive
# This file just sets a few utility functions to be used by the code.
# randompw generates a random password as per the configuration file.
alias randompw="cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $PASSWORD_LENGTH | head -n 1"

22
distillery/lib/lib.sh Normal file
View file

@ -0,0 +1,22 @@
#!/bin/bash
set -e
# This file will load all the library functions needed by the various scripts.
# It should be 'source'd only, if it is not we bail out here.
if [[ "$0" = "$BASH_SOURCE" ]]; then
echo "This file should not be executed directly, it should be 'source'd only. "
exit 1;
fi
# Set variables for the script_dir and the lib_dir
SCRIPT_DIR="$(pwd)"
LIB_DIR="$SCRIPT_DIR/lib"
# Next, we load a bunch of utility functions stored in lib/lib_<number>_<system>.sh
# These contain functionality used in the various scripts.
source "$LIB_DIR/00_init.sh";
source "$LIB_DIR/10_config.sh";
source "$LIB_DIR/20_sql.sh";
source "$LIB_DIR/30_slug.sh";
source "$LIB_DIR/40_templates.sh";
source "$LIB_DIR/50_utils.sh";