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