LibrePortal/scripts/backup/engine/kopia_snapshots.sh
librelad 34bd6d7936 feat(backup): kopia + borg system-config adapters (engine parity)
Mirror the restic system-config adapters for the other two engines, each in that
engine's own convention, so system backup/restore/status/retention work on any
location regardless of engine:

- kopia: BackupSystemToLocation (--tags system:config), SystemSnapshotsJson
  (filter tag system:config), RestoreSystemLatest, ForgetSystem (per-source policy
  on $configs_dir + maintenance).
- borg: BackupSystemToLocation (archive system-<host>-<ts>, comment system=config;
  no app is named "system" so the namespace can't collide), SystemSnapshotsJson
  (--glob-archives system-<host>-*), RestoreSystemLatest, ForgetSystem (prune the
  system-<host>-* glob).

No dispatcher change needed — engineBackupSystem/SystemSnapshotsJson/
RestoreSystemLatest/ForgetSystem already resolve <engine><fn> per location. All
three engines now define the full set; syntax clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-26 00:56:00 +01:00

125 lines
3.2 KiB
Bash

#!/bin/bash
# Reshape `kopia snapshot list --json` to match the restic-style {short_id,
# time, hostname, tags, paths} schema the frontend consumes.
kopiaSnapshotsJson()
{
local idx="$1"
local app_filter="$2"
local host_filter="$3"
kopiaEnvExport "$idx" || return 1
local args=(snapshot list --all --json)
local raw
raw=$(runBackupOp kopia "${args[@]}" 2>/dev/null)
local rc=$?
kopiaEnvUnset
[[ $rc -ne 0 || -z "$raw" ]] && { echo "[]"; return $rc; }
local jq_filter='[.[] | {
id: .id,
short_id: (.id[0:8]),
time: .startTime,
hostname: .source.host,
tags: ((.tags // []) | map(sub(":"; "="))),
paths: [.source.path]
}]'
if [[ -n "$app_filter" ]]; then
jq_filter='[.[] | select(any(.tags[]?; . == "app:'"$app_filter"'"))] | '"$jq_filter"
fi
if [[ -n "$host_filter" ]]; then
jq_filter='[.[] | select(.source.host == "'"$host_filter"'")] | '"$jq_filter"
fi
if command -v jq >/dev/null 2>&1; then
echo "$raw" | jq -c "$jq_filter"
else
echo "$raw"
fi
}
kopiaSystemSnapshotsJson()
{
local idx="$1"
local host_filter="$2"
kopiaEnvExport "$idx" || return 1
local raw
raw=$(runBackupOp kopia snapshot list --all --json 2>/dev/null)
local rc=$?
kopiaEnvUnset
[[ $rc -ne 0 || -z "$raw" ]] && { echo "[]"; return $rc; }
local jq_filter='[.[] | {
id: .id,
short_id: (.id[0:8]),
time: .startTime,
hostname: .source.host,
tags: ((.tags // []) | map(sub(":"; "="))),
paths: [.source.path]
}]'
jq_filter='[.[] | select(any(.tags[]?; . == "system:config"))] | '"$jq_filter"
if [[ -n "$host_filter" ]]; then
jq_filter='[.[] | select(.source.host == "'"$host_filter"'")] | '"$jq_filter"
fi
if command -v jq >/dev/null 2>&1; then
echo "$raw" | jq -c "$jq_filter"
else
echo "$raw"
fi
}
kopiaRestoreSystemLatest()
{
local idx="$1"
local target_dir="$2"
local host="${3:-$CFG_INSTALL_NAME}"
local json snapshot_id
json=$(kopiaSystemSnapshotsJson "$idx" "$host")
if command -v jq >/dev/null 2>&1; then
snapshot_id=$(echo "$json" | jq -r 'sort_by(.time) | last | .id // empty')
else
snapshot_id=$(echo "$json" | grep -oE '"id":\s*"[^"]+"' | tail -1 | cut -d'"' -f4)
fi
if [[ -z "$snapshot_id" ]]; then
isError "No system-config snapshot found in $(resticLocationName "$idx") for host=$host"
return 1
fi
# Whole-snapshot restore into staging (no include subpath).
kopiaRestoreSnapshot "$idx" "$snapshot_id" "$target_dir"
}
kopiaSnapshotLatestId()
{
local idx="$1"
local app_name="$2"
local host="${3:-$CFG_INSTALL_NAME}"
local json
json=$(kopiaSnapshotsJson "$idx" "$app_name" "$host")
if command -v jq >/dev/null 2>&1; then
echo "$json" | jq -r 'sort_by(.time) | last | .id // empty'
else
echo "$json" | grep -oE '"id":\s*"[^"]+"' | tail -1 | cut -d'"' -f4
fi
}
kopiaSnapshotListFiles()
{
local idx="$1"
local snapshot_id="$2"
kopiaEnvExport "$idx" || return 1
runBackupOp kopia snapshot list "$snapshot_id" --json 2>/dev/null
local rc=$?
kopiaEnvUnset
return $rc
}