M5: upgrade + backup/restore stages green (custom-html); backup-bot-two oneshot
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
3-stage run green (install/upgrade/backup), clean teardown. backupbot deployed via reconcile oneshot; PTY (script) for abra backup/restore; -m for secret generate (no value leak). M5 CLAIMED. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
49
modules/backupbot.nix
Normal file
49
modules/backupbot.nix
Normal file
@ -0,0 +1,49 @@
|
||||
# backup-bot-two (M5): the Co-op Cloud backup service. `abra app backup create <app>` / restore
|
||||
# talk to it; it snapshots volumes labelled `backupbot.backup=true` into a local restic repo.
|
||||
# Idempotent-reconcile oneshot (same pattern as proxy/drone). restic_password is abra-generated
|
||||
# (class-B-style internal secret) and kept stable across reconciles (only generated if missing).
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
reconcile = pkgs.writeShellApplication {
|
||||
name = "cc-ci-reconcile-backupbot";
|
||||
runtimeInputs = with pkgs; [ abra docker gnused gnugrep coreutils git ];
|
||||
text = ''
|
||||
DOMAIN="backups.ci.commoninternet.net" # identity/stack name only; no web route
|
||||
ENV_FILE="$HOME/.abra/servers/default/$DOMAIN.env"
|
||||
|
||||
abra server ls -m -n >/dev/null 2>&1 || abra server add --local -n || true
|
||||
abra recipe fetch backup-bot-two -n >/dev/null
|
||||
|
||||
[ -f "$ENV_FILE" ] || abra app new backup-bot-two -s default -D "$DOMAIN" -n
|
||||
|
||||
set_env() {
|
||||
sed -i -E "/^[[:space:]]*#?[[:space:]]*$1=/d" "$ENV_FILE"
|
||||
printf '%s=%s\n' "$1" "$2" >> "$ENV_FILE"
|
||||
}
|
||||
set_env RESTIC_REPOSITORY /backups/restic
|
||||
set_env SECRET_RESTIC_PASSWORD_VERSION v1
|
||||
set_env CRONJOB_VERSION v1
|
||||
|
||||
have_secret() { docker secret ls --format '{{.Name}}' | grep -q "_$1_v1$"; }
|
||||
# -m avoids the TTY/table (ioctl) path; redirect stdout so generated values never hit logs (D6).
|
||||
have_secret restic_password || abra app secret generate "$DOMAIN" --all -m -n >/dev/null
|
||||
|
||||
abra app deploy "$DOMAIN" -n -C
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
systemd.services.deploy-backupbot = {
|
||||
description = "Reconcile backup-bot-two (volume backups via restic) via abra";
|
||||
after = [ "swarm-init.service" "docker.service" "network-online.target" ];
|
||||
requires = [ "swarm-init.service" "docker.service" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment.HOME = "/root";
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${reconcile}/bin/cc-ci-reconcile-backupbot";
|
||||
};
|
||||
};
|
||||
}
|
||||
@ -7,7 +7,7 @@ let
|
||||
pyEnv = pkgs.python3.withPackages (ps: with ps; [ pytest playwright ]);
|
||||
ccciRun = pkgs.writeShellApplication {
|
||||
name = "cc-ci-run";
|
||||
runtimeInputs = [ pyEnv pkgs.abra pkgs.docker pkgs.git pkgs.coreutils ];
|
||||
runtimeInputs = [ pyEnv pkgs.abra pkgs.docker pkgs.git pkgs.coreutils pkgs.util-linux ];
|
||||
text = ''
|
||||
export PLAYWRIGHT_BROWSERS_PATH=${pkgs.playwright-driver.browsers}
|
||||
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
||||
|
||||
Reference in New Issue
Block a user