Polish: daily snapshot rotation #86
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env bash
|
||||
# rotate-snapshots.sh — create today's daily snapshot + prune snapshots
|
||||
# older than N days. Idempotent: safe to run repeatedly on the same day.
|
||||
#
|
||||
# Snapshots created by this script are named `daily-YYYY-MM-DD`. Only
|
||||
# snapshots matching that prefix are candidates for pruning — hand-created
|
||||
# snapshots (release-anchor, pre-migration, etc.) are never touched.
|
||||
#
|
||||
# Env / args:
|
||||
# CLAWSTOR_BIN path to claw-store binary (default: ~/clawstor-deploy/claw-store)
|
||||
# CLAWSTOR_CONFIG path to config.toml (default: ~/clawstor-deploy/config.toml)
|
||||
# RETAIN_DAYS snapshots older than this many days get pruned (default: 30)
|
||||
# DRY_RUN=1 print what would happen, no side effects
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLAWSTOR_BIN=${CLAWSTOR_BIN:-$HOME/clawstor-deploy/claw-store}
|
||||
CLAWSTOR_CONFIG=${CLAWSTOR_CONFIG:-$HOME/clawstor-deploy/config.toml}
|
||||
RETAIN_DAYS=${RETAIN_DAYS:-30}
|
||||
DRY_RUN=${DRY_RUN:-0}
|
||||
|
||||
if [ ! -x "$CLAWSTOR_BIN" ]; then
|
||||
echo "error: $CLAWSTOR_BIN not executable" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CS="$CLAWSTOR_BIN --config $CLAWSTOR_CONFIG"
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
NAME="daily-$TODAY"
|
||||
|
||||
echo "── rotate-snapshots ────────────────────────────────"
|
||||
echo "today: $NAME"
|
||||
echo "retention: $RETAIN_DAYS days"
|
||||
|
||||
# Create today's snapshot. Idempotent: `cluster-snapshot-create` errors
|
||||
# if the name already exists — we treat that as success.
|
||||
if [ "$DRY_RUN" = "1" ]; then
|
||||
echo "dry-run: would create snapshot $NAME"
|
||||
else
|
||||
if $CS cluster-snapshot-create --name "$NAME" 2>&1 | tail -6; then
|
||||
echo "created: $NAME"
|
||||
else
|
||||
echo "already exists (idempotent): $NAME"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Compute cutoff — POSIX date arithmetic (no GNU date extensions needed
|
||||
# because we do the math on the epoch integer).
|
||||
NOW_EPOCH=$(date +%s)
|
||||
CUTOFF_EPOCH=$((NOW_EPOCH - RETAIN_DAYS * 86400))
|
||||
|
||||
# Snapshot list format:
|
||||
# CREATED_AT NAME BLOBS SIZE
|
||||
# 1784046443 daily-2026-07-14 4 224
|
||||
# So awk field 1 = epoch, field 2 = name.
|
||||
PRUNE_LIST=$($CS cluster-snapshot-list 2>/dev/null | awk -v cutoff="$CUTOFF_EPOCH" '
|
||||
NR > 4 && $2 ~ /^daily-/ && $1 < cutoff { print $2 }
|
||||
')
|
||||
|
||||
echo
|
||||
if [ -z "$PRUNE_LIST" ]; then
|
||||
echo "nothing to prune"
|
||||
else
|
||||
echo "prune candidates (older than $RETAIN_DAYS days):"
|
||||
echo "$PRUNE_LIST" | sed 's/^/ /'
|
||||
if [ "$DRY_RUN" = "1" ]; then
|
||||
echo "dry-run: no snapshots deleted"
|
||||
else
|
||||
echo "$PRUNE_LIST" | while read -r snap; do
|
||||
$CS cluster-snapshot-delete --name "$snap" 2>&1 | tail -1
|
||||
done
|
||||
fi
|
||||
fi
|
||||
echo "────────────────────────────────────────────────────"
|
||||
@@ -11,6 +11,7 @@ Systemd **user** units for clawstor.
|
||||
| `clawstor-scrub.service` + `clawstor-scrub.timer` | Weekly (Sun 04:00) BLAKE3 verify every chunk against its manifest. Non-zero exit = integrity failure — surfaced by systemd status. |
|
||||
| `clawstor-gc.service` + `clawstor-gc.timer` | Nightly (03:30) orphan-chunk sweep. Override ExecStart via drop-in to add `--evict-to-gb N` for size-cap fleets. Sequenced before the Sunday scrub so scrub reads a fresh layout. |
|
||||
| `clawstor-ref-sweep.service` + `clawstor-ref-sweep.timer` | Nightly (03:15) Gitea live-refs poll + stale-fingerprint report. Set `GITEA_TOKEN` via a drop-in for private repos. Report-only (dry-run) — deletion is a follow-on. |
|
||||
| `clawstor-snapshot-rotate.service` + `clawstor-snapshot-rotate.timer` | Daily (02:00) create `daily-YYYY-MM-DD` snapshot + prune snapshots older than `RETAIN_DAYS` (default 30). Only touches snapshots whose name matches `daily-*` — hand-created ones survive. |
|
||||
|
||||
## Install `clawstor-fuse.service`
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=Clawstor daily snapshot rotation (create today + prune > N days)
|
||||
Documentation=https://git.redclaw.dev/clawverse/clawstor
|
||||
After=clawstor-cluster.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
Environment=RETAIN_DAYS=30
|
||||
ExecStart=%h/clawstor-deploy/scripts/rotate-snapshots.sh
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Daily Clawstor snapshot rotation
|
||||
Documentation=https://git.redclaw.dev/clawverse/clawstor
|
||||
|
||||
[Timer]
|
||||
# 02:00 local — ahead of ref-sweep (03:15) + gc (03:30) so the fresh
|
||||
# snapshot's pins protect its blobs from tonight's eviction.
|
||||
OnCalendar=*-*-* 02:00:00
|
||||
Persistent=true
|
||||
Unit=clawstor-snapshot-rotate.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Reference in New Issue
Block a user