Browse Source
new: [mariadb] added charm.
new: [mariadb] added charm.
Signed-off-by: Valentin Lab <valentin.lab@kalysto.org>dev1
Valentin Lab
4 years ago
6 changed files with 313 additions and 0 deletions
-
61mariadb/hooks/init
-
62mariadb/hooks/mysql_database-relation-joined
-
44mariadb/hooks/schedule_command-relation-joined
-
76mariadb/lib/common
-
18mariadb/metadata.yml
-
52mariadb/resources/bin/mysql-backup
@ -0,0 +1,61 @@ |
|||
#!/bin/bash |
|||
|
|||
## Init is run on host |
|||
## For now it is run every time the script is launched, but |
|||
## it should be launched only once after build. |
|||
|
|||
## Accessible variables are: |
|||
## - SERVICE_NAME Name of current service |
|||
## - DOCKER_BASE_IMAGE Base image from which this service might be built if any |
|||
## - SERVICE_DATASTORE Location on host of the DATASTORE of this service |
|||
## - SERVICE_CONFIGSTORE Location on host of the CONFIGSTORE of this service |
|||
|
|||
|
|||
. lib/common |
|||
|
|||
set -e |
|||
|
|||
if [ "${HOST_DATASTORE+x}" ]; then |
|||
export HOST_DB_PASSFILE="$HOST_DATASTORE/${SERVICE_NAME}$DB_DATADIR/my.cnf" |
|||
else |
|||
export HOST_DB_PASSFILE="$CLIENT_DB_PASSFILE" |
|||
fi |
|||
|
|||
|
|||
if ! [ -d "$HOST_DATASTORE/${SERVICE_NAME}$DB_DATADIR" ]; then |
|||
|
|||
MYSQL_ROOT_PASSWORD="$(gen_password)" |
|||
|
|||
debug docker run -e "MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD" \ |
|||
--rm \ |
|||
-v "$DATA_DIR:$DB_DATADIR" \ |
|||
--entrypoint /bin/bash "$DOCKER_BASE_IMAGE" |
|||
docker run -e "MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD" \ |
|||
--rm \ |
|||
-v "$DATA_DIR:$DB_DATADIR" \ |
|||
--entrypoint /bin/bash "$DOCKER_BASE_IMAGE" -c ' |
|||
mysqld() { |
|||
echo "diverted mysqld call..." >&2; |
|||
echo "$*" | grep -E "(--help|--skip-networking)" >/dev/null 2>&1 || return; |
|||
echo " .. Allowing call." >&2; |
|||
/usr/sbin/mysqld "$@"; |
|||
} |
|||
export -f mysqld; |
|||
/docker-entrypoint.sh mysqld' || true |
|||
## docker errorlevel is still 0 even if it failed. |
|||
## AND we must ignore mysqld error ! |
|||
[ "$(find "$DATA_DIR" \ |
|||
-maxdepth 0 -type d -empty 2>/dev/null)" ] && { |
|||
err "Docker run probably failed to do it's job." |
|||
exit 1 |
|||
} |
|||
|
|||
## XXXvlab: this won't help support multiple project running on the |
|||
## same host |
|||
cat <<EOF > "$HOST_DB_PASSFILE" |
|||
[client] |
|||
password=$MYSQL_ROOT_PASSWORD |
|||
EOF |
|||
chmod 600 "$HOST_DB_PASSFILE" |
|||
info "New root password for mysql. " |
|||
fi |
@ -0,0 +1,62 @@ |
|||
#!/bin/bash |
|||
|
|||
## When writing relation script, remember: |
|||
## - they should be idempotents |
|||
## - they can be launched while the dockers is already up |
|||
## - they are launched from the host |
|||
## - the target of the link is launched first, and get a chance to ``relation-set`` |
|||
## - both side of the scripts get to use ``relation-get``. |
|||
|
|||
DBNAME=$(relation-get dbname 2>/dev/null) || { |
|||
DBNAME="$BASE_SERVICE_NAME" |
|||
relation-set dbname "$DBNAME" |
|||
} |
|||
|
|||
USER=$(relation-get user 2>/dev/null) || { |
|||
USER="$BASE_SERVICE_NAME" |
|||
relation-set user "$USER" |
|||
} |
|||
|
|||
|
|||
PASSWORD="$(relation-get password 2>/dev/null)" |
|||
|
|||
. lib/common |
|||
|
|||
set -e |
|||
|
|||
## is there a previous password set for user $USER ? |
|||
|
|||
NO_PREVIOUS_PASS= |
|||
PREVIOUS_PASSWORD_PATH="$state_tmpdir/$SERVICE_NAME/pwd/$USER" |
|||
PREVIOUS_PASSWORD=$(cat "$PREVIOUS_PASSWORD_PATH" 2>/dev/null) || NO_PREVIOUS_PASS=true |
|||
|
|||
if PASSWORD="$(relation-get password 2>/dev/null)"; then |
|||
if [ -z "$NO_PREVIOUS_PASS" -a "$PREVIOUS_PASSWORD" != "$PASSWORD" ]; then |
|||
die "Inconsistent password specification for user '$USER' on ${DARKYELLOW}$TARGET_SERVICE_NAME$NORMAL." |
|||
fi |
|||
else |
|||
if [ "$PREVIOUS_PASSWORD" ]; then |
|||
PASSWORD="${PREVIOUS_PASSWORD}" |
|||
else |
|||
PASSWORD="$(gen_password)" |
|||
info "Generated a new password for user '$USER'." |
|||
fi |
|||
fi |
|||
|
|||
|
|||
|
|||
ensure_db_docker_running || exit 1 |
|||
if [ "$?" == 0 ] && check_access "$DBNAME" "$USER" "$PASSWORD"; then |
|||
info "Access to database '$DBNAME' from user '$USER' verified working." |
|||
exit 0 |
|||
fi |
|||
|
|||
|
|||
db_create "$DBNAME" |
|||
|
|||
db_grant_rights "$DBNAME" "$USER" "$PASSWORD" |
|||
info "Granted rights on database '$DBNAME' to user '$USER'." |
|||
|
|||
|
|||
|
|||
relation-set password "$PASSWORD" |
@ -0,0 +1,44 @@ |
|||
#!/bin/bash |
|||
|
|||
## When writing relation script, remember: |
|||
## - they should be idempotents |
|||
## - they can be launched while the dockers is already up |
|||
## - they are launched from the host |
|||
## - the target of the link is launched first, and get a chance to ``relation-set`` |
|||
## - both side of the scripts get to use ``relation-get``. |
|||
|
|||
. lib/common |
|||
|
|||
|
|||
set -e |
|||
|
|||
## XXXvlab: should use container name here so that it could support |
|||
## multiple mysql |
|||
label=${SERVICE_NAME}-mysql-backup |
|||
DST=$CONFIGSTORE/$TARGET_SERVICE_NAME/etc/cron/$label |
|||
schedule=$(relation-get schedule) |
|||
|
|||
if ! echo "$schedule" | egrep '^\s*(([0-9/,*-]+\s+){4,4}[0-9/,*-]+|@[a-z]+)\s*$' >/dev/null 2>&1; then |
|||
err "Unrecognized schedule '$schedule'." |
|||
exit 1 |
|||
fi |
|||
|
|||
## Warning: using '\' in heredoc will be removed in the final cron file, which |
|||
## is totally wanted: cron does not support multilines. |
|||
|
|||
## Warning: 'docker -v' will use HOST directory even if launched from |
|||
## 'cron' container. |
|||
file_put "$DST" <<EOF |
|||
COMPOSE_LAUNCHER_OPTS=$COMPOSE_LAUNCHER_OPTS |
|||
|
|||
$schedule root lock $label -D -p 10 -c "\ |
|||
docker run --rm \ |
|||
-e MYSQLHOST="${SERVICE_NAME}" \ |
|||
--network ${PROJECT_NAME}_default \ |
|||
-v \"$LOCAL_DB_PASSFILE\":/root/.my.cnf \ |
|||
-v \"$HOST_CHARM_STORE/${CHARM_REL_PATH#${CHARM_STORE}/}/resources/bin/mysql-backup:/usr/sbin/mysql-backup\" \ |
|||
-v \"$SERVICE_DATASTORE/var/backups/mysql:/var/backups/mysql\" \ |
|||
--entrypoint mysql-backup \ |
|||
\"$DOCKER_BASE_IMAGE\"" 2>&1 | ts '\%F \%T \%Z' >> /var/log/cron/${label}_script.log |
|||
EOF |
|||
chmod +x "$DST" |
@ -0,0 +1,76 @@ |
|||
# -*- mode: shell-script -*- |
|||
|
|||
include pretty |
|||
|
|||
export DB_NAME="$SERVICE_NAME" ## general type of database (ie: postgres/mysql...) |
|||
export DB_DATADIR=/var/lib/mysql |
|||
|
|||
export DATA_DIR=$SERVICE_DATASTORE$DB_DATADIR |
|||
|
|||
export LOCAL_DB_PASSFILE="$DATA_DIR/my.cnf" |
|||
export CLIENT_DB_PASSFILE=/root/.my.cnf |
|||
|
|||
|
|||
is_db_locked() { |
|||
local host_db_volume |
|||
if [ "${HOST_DATASTORE+x}" ]; then |
|||
host_db_volume="$HOST_DATASTORE/${SERVICE_NAME}" |
|||
else |
|||
host_db_volume="$DATASTORE/${SERVICE_NAME}" |
|||
fi |
|||
## ``are_files_locked_in_dir`` doesn't work with mysql here as |
|||
## tested |
|||
is_volume_used "$host_db_volume" |
|||
} |
|||
|
|||
|
|||
_set_db_params() { |
|||
local docker_ip="$1" docker_network="$2" |
|||
|
|||
if [ "${HOST_DATASTORE+x}" ]; then |
|||
export HOST_DB_PASSFILE="$HOST_DATASTORE/${SERVICE_NAME}$DB_DATADIR/my.cnf" |
|||
else |
|||
export HOST_DB_PASSFILE="$CLIENT_DB_PASSFILE" |
|||
fi |
|||
|
|||
[ -f "$CLIENT_DB_PASSFILE" ] || touch "$CLIENT_DB_PASSFILE" |
|||
|
|||
server_docker_opts+=() |
|||
db_docker_opts+=("--network" "$docker_network") |
|||
db_cmd_opts+=("-h" "$docker_ip") |
|||
check_command="SELECT 1;" |
|||
} |
|||
|
|||
_set_server_db_params() { |
|||
server_docker_opts+=() |
|||
} |
|||
|
|||
|
|||
ddb () { dcmd mysql "$@"; } |
|||
|
|||
|
|||
## |
|||
## Entrypoints |
|||
## |
|||
|
|||
|
|||
db_create () { |
|||
local dbname="$1" |
|||
## Using this instead of pipes is important so that trap works |
|||
debug "Create if not exists '$dbname' database..." |
|||
ddb < <(echo "CREATE DATABASE IF NOT EXISTS \`$dbname\`") |
|||
} |
|||
|
|||
db_grant_rights () { |
|||
local dbname="$1" user="$2" password="$3" |
|||
## Using this instead of pipes is important so that trap works |
|||
debug "Grant all on $dbname to $user" |
|||
ddb < <(echo "GRANT ALL ON \`$dbname\`.* TO '$user'@'%' IDENTIFIED BY '$password'") |
|||
} |
|||
|
|||
check_access() { |
|||
local dbname="$1" user="$2" password="$3" |
|||
## Using this instead of pipes is important so that trap works |
|||
debug "Check if credentials for user '$user' can access database '$dbname'" |
|||
ddb --user="$user" --password="$password" "$dbname" < <(echo "SELECT 1") |
|||
} |
@ -0,0 +1,18 @@ |
|||
name: MariaDB |
|||
## From: mysql Ver 15.1 Distrib 10.0.21-MariaDB |
|||
docker-image: docker.0k.io/mariadb:1.0.0 |
|||
maintainer: "Valentin Lab <valentin.lab@kalysto.org>" |
|||
provides: |
|||
mysql-database: |
|||
data-resources: |
|||
- /var/lib/mysql |
|||
- /var/backups/mysql |
|||
|
|||
uses: |
|||
schedule-command: |
|||
constraint: optional |
|||
auto: pair |
|||
solves: |
|||
backup: "Automatic regular dumps for backuping purpose" |
|||
default-options: |
|||
schedule: "31 * * * *" ## schedule backup every hour |
@ -0,0 +1,52 @@ |
|||
#!/bin/bash |
|||
|
|||
m() { |
|||
mysql "${mysql_opts[@]}" -Bs "$@" |
|||
} |
|||
|
|||
md() { |
|||
mysqldump "${mysql_opts[@]}" "$@" |
|||
} |
|||
|
|||
mysql_databases() { |
|||
echo "SHOW DATABASES" | m |
|||
} |
|||
|
|||
mysql_tables() { |
|||
local db="$1" |
|||
echo "SHOW TABLES" | m "$db" |
|||
} |
|||
|
|||
mysql_opts=() |
|||
if [ "$MYSQLHOST" ]; then |
|||
mysql_opts+=(-h "$MYSQLHOST") |
|||
fi |
|||
|
|||
|
|||
DBS=($(mysql_databases)) || exit 1 |
|||
|
|||
mkdir -p /var/backups/mysql |
|||
|
|||
for db in "${DBS[@]}"; do |
|||
if [[ "$db" == "information_schema" || "$db" == "performance_schema" || "$db" == "mysql" ]]; then |
|||
continue |
|||
fi |
|||
echo "Dumping database $db..." >&2 |
|||
# omitting all the rotation logic |
|||
dst=/"var/backups/mysql/$db" |
|||
[ -d "$dst.old" ] && rm -rf "$dst.old" |
|||
[ -d "$dst" ] && mv "$dst" "$dst.old" |
|||
mkdir -p "$dst.inprogress" |
|||
(( start = SECONDS )) |
|||
md "$db" --routines --no-data --add-drop-database --database "$db" | gzip --rsyncable > "$dst.inprogress/schema.sql.gz" |
|||
tables=$(mysql_tables "$db") |
|||
for table in $tables; do |
|||
backup_file="$dst.inprogress/${table}.sql.gz" |
|||
echo " Dumping $table into ${backup_file}" |
|||
md "$db" "$table" | gzip --rsyncable > "$backup_file" || break |
|||
done |
|||
mv "$dst.inprogress" "$dst" |
|||
[ -d "$dst.old" ] && rm -rf "$dst.old" |
|||
(( elapsed = SECONDS - start )) |
|||
echo " ..dumped $db to $dst ($(du -sh "$dst" | cut -f 1) in ${elapsed}s)" >&2 |
|||
done |
Write
Preview
Loading…
Cancel
Save
Reference in new issue