forked from 0k/0k-charms
Browse Source
new: [rsync-backup] add charm
new: [rsync-backup] add charm
Signed-off-by: Valentin Lab <valentin.lab@kalysto.org>0k/dev/master
Valentin Lab
5 years ago
8 changed files with 227 additions and 2 deletions
-
4rsync-backup-target/build/src/etc/ssh/sshd_config
-
27rsync-backup/build/Dockerfile
-
71rsync-backup/build/entrypoint.sh
-
35rsync-backup/hooks/backup-relation-joined
-
22rsync-backup/hooks/init
-
48rsync-backup/hooks/schedule_command-relation-joined
-
5rsync-backup/lib/common
-
17rsync-backup/metadata.yml
@ -0,0 +1,27 @@ |
|||
FROM alpine:3.9 |
|||
|
|||
MAINTAINER Valentin Lab <valentin.lab@kalysto.org> |
|||
|
|||
|
|||
RUN apk add bash rsync sudo openssh-client |
|||
|
|||
# RUN apt-get update && \ |
|||
# DEBIAN_FRONTEND=noninteractive apt-get install --force-yes -y --no-install-recommends rsync sudo openssh-client && \ |
|||
# apt-get clean && \ |
|||
# rm -rf /var/lib/apt/lists/* |
|||
|
|||
## New user/group rsync/rsync with home dir in /var/lib/rsync |
|||
RUN mkdir -p /var/lib/rsync && \ |
|||
addgroup -S rsync && \ |
|||
adduser -S rsync -h /var/lib/rsync -G rsync && \ |
|||
chown rsync:rsync /var/lib/rsync |
|||
|
|||
## New user/group rsync/rsync with home dir in /var/lib/rsync |
|||
# RUN mkdir -p /var/lib/rsync && \ |
|||
# groupadd -r rsync && \ |
|||
# useradd -r rsync -d /var/lib/rsync -g rsync && \ |
|||
# chown rsync:rsync /var/lib/rsync |
|||
|
|||
COPY ./entrypoint.sh /entrypoint.sh |
|||
|
|||
ENTRYPOINT [ "/entrypoint.sh" ] |
@ -0,0 +1,71 @@ |
|||
#!/bin/bash |
|||
|
|||
[ "$UID" != "0" ] && echo "You must be root." 2>&1 && exit 1 |
|||
|
|||
## |
|||
## code |
|||
## |
|||
|
|||
|
|||
user=rsync |
|||
src="$1" |
|||
|
|||
if [ -z "$src" ]; then |
|||
echo "You must provide a source directory as first argument." >&2 |
|||
exit 1 |
|||
fi |
|||
|
|||
if ! [ -d "$src" ]; then |
|||
echo "Folder '$src' not found, please provide a valid directory as first argument." >&2 |
|||
exit 1 |
|||
fi |
|||
|
|||
dest="$2" |
|||
|
|||
if [ -z "$dest" ]; then |
|||
echo "You must provide a host as target." >&2 |
|||
exit 1 |
|||
fi |
|||
|
|||
|
|||
rsync_options=(${RSYNC_OPTIONS:-}) |
|||
ssh_options=(${SSH_OPTIONS:--o StrictHostKeyChecking=no}) |
|||
|
|||
|
|||
if echo "$dest" | grep "/" >/dev/null 2>&1; then |
|||
rsync_options=("--bwlimit" "$(echo "$dest" | cut -f 2 -d "/")" "${rsync_options[@]}") |
|||
dest="$(echo "$dest" | cut -f 1 -d "/")" |
|||
fi |
|||
|
|||
if echo "$dest" | grep ":" >/dev/null 2>&1; then |
|||
ssh_options=("-p" "$(echo "$dest" | cut -f 2 -d ":")" "${ssh_options[@]}") |
|||
dest="$(echo "$dest" | cut -f 1 -d ":")" |
|||
fi |
|||
|
|||
|
|||
hostname=$(hostname) |
|||
hostname=${LABEL_HOSTNAME:-$hostname} |
|||
|
|||
dest_path="/var/mirror/$hostname" |
|||
|
|||
touch /etc/rsync/exclude-patterns |
|||
touch /etc/rsync/include-patterns |
|||
|
|||
if ! [ -s "/etc/rsync/include-patterns" ]; then |
|||
echo "Nothing to do as /etc/rsync/include-patterns is empty." |
|||
exit 0 |
|||
fi |
|||
|
|||
cmd=(/usr/bin/rsync "${rsync_options[@]}" -azvA |
|||
-e "sudo -u $user ssh ${ssh_options[*]}" |
|||
--include-from /etc/rsync/include-patterns |
|||
--exclude-from /etc/rsync/exclude-patterns |
|||
--delete --partial --partial-dir .rsync-partial |
|||
--numeric-ids "$src/" "$user@$dest":"$dest_path") |
|||
|
|||
rsync_uid_gid=$(stat -c "%u:%g" "/var/lib/rsync") |
|||
chown "$rsync_uid_gid" "/var/lib/rsync/.ssh" -R |
|||
|
|||
echo "${cmd[@]}" |
|||
|
|||
exec "${cmd[@]}" |
@ -0,0 +1,35 @@ |
|||
#!/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 |
|||
|
|||
exclude_patterns=$(relation-get "exclude-patterns") || true |
|||
include_patterns=$(relation-get "include-patterns") || true |
|||
include_patterns=${include_patterns:-"- /"} |
|||
|
|||
while read-0 pattern; do |
|||
if [[ "$pattern" != /* ]]; then |
|||
err "Invalid pattern '$pattern' provided as ${WHITE}exclude-pattern${NORMAL}, " \ |
|||
"in relation's options." |
|||
exit 1 |
|||
fi |
|||
echo "/$BASE_SERVICE_NAME$pattern" |
|||
done < <(e "$exclude_patterns" | shyaml get-values-0) >> "$RSYNC_EXCLUDE_PATTERNS" |
|||
|
|||
while read-0 pattern; do |
|||
if [[ "$pattern" != /* ]]; then |
|||
err "Invalid pattern '$pattern' provided as ${WHITE}exclude-pattern${NORMAL}, " \ |
|||
"in relation's options." |
|||
exit 1 |
|||
fi |
|||
echo "/$BASE_SERVICE_NAME$pattern" |
|||
done < <(e "$include_patterns" | shyaml get-values-0) >> "$RSYNC_INCLUDE_PATTERNS" |
|||
|
@ -0,0 +1,22 @@ |
|||
#!/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 |
|||
|
|||
|
|||
RSYNC_EXCLUDE_PATTERNS="$SERVICE_CONFIGSTORE/etc/rsync/exclude-patterns" |
|||
|
|||
mkdir -p "${RSYNC_EXCLUDE_PATTERNS%/*}" |
|||
echo "/*/" > "$RSYNC_EXCLUDE_PATTERNS" ## start a new |
|||
echo > "$RSYNC_INCLUDE_PATTERNS" ## start a new |
@ -0,0 +1,48 @@ |
|||
#!/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 |
|||
|
|||
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 |
|||
|
|||
private_key=$(options-get private-key) || exit 1 |
|||
target=$(options-get target) || exit 1 |
|||
ident=$(options-get ident) || exit 1 |
|||
|
|||
home=/var/lib/rsync |
|||
local_path_key=$home/.ssh |
|||
host_path_key="$SERVICE_CONFIGSTORE${local_path_key}" |
|||
|
|||
echo "$private_key" | file_put "$host_path_key/id_rsa" |
|||
chmod 600 "$host_path_key/id_rsa" |
|||
|
|||
label="${SERVICE_NAME}" |
|||
DST=$CONFIGSTORE/$TARGET_CHARM_NAME/etc/cron/$label |
|||
|
|||
## Warning: using '\' in heredoc will be removed in the final cron file, which |
|||
## is totally wanted: cron does not support multilines. |
|||
file_put "$DST" <<EOF |
|||
$schedule root lock $label -v -D -p 10 -k -c "\ |
|||
docker run --rm \ |
|||
-e LABEL_HOSTNAME=\"$ident\" \ |
|||
-v \"$RSYNC_CONFIG_DIR:/etc/rsync\" \ |
|||
-v \"$host_path_key:$local_path_key\" \ |
|||
-v \"$HOST_DATASTORE:/mnt/source\" \ |
|||
--network ${PROJECT_NAME}_default \ |
|||
\"$DOCKER_BASE_IMAGE\" \ |
|||
/mnt/source \"$target\"" 2>&1 | ts '\%F \%T' >> /var/log/cron/${label}_script.log |
|||
EOF |
|||
chmod +x "$DST" |
@ -0,0 +1,5 @@ |
|||
|
|||
|
|||
RSYNC_CONFIG_DIR="$SERVICE_CONFIGSTORE/etc/rsync" |
|||
RSYNC_INCLUDE_PATTERNS="$RSYNC_CONFIG_DIR/include-patterns" |
|||
RSYNC_EXCLUDE_PATTERNS="$RSYNC_CONFIG_DIR/exclude-patterns" |
@ -0,0 +1,17 @@ |
|||
description: Rsync Backup Service |
|||
type: run-once |
|||
|
|||
provides: |
|||
backup: |
|||
|
|||
uses: |
|||
schedule-command: |
|||
constraint: required |
|||
auto: summon |
|||
solves: |
|||
missing-feature: "scheduling of backups" |
|||
default-options: |
|||
## backup every day on random time |
|||
schedule: !bash-stdout | |
|||
printf "%d %d * * *" "$((RANDOM % 60))" "$((RANDOM % 6))" |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue