kamal-backup 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/kamal_backup/app.rb +4 -0
- data/lib/kamal_backup/databases/postgres.rb +54 -0
- data/lib/kamal_backup/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 341913561478a45fc1dd88b376daae6977a180eba43739963c6e9055d5c064ab
|
|
4
|
+
data.tar.gz: 69a572c55cc9b33fcd0ab0a3918111cf7c3e26f3519d01de855c2fa73de08b2d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: af86080b3f6524990cd0df83bed996370e4cff186aaea4338e8ab9e9ddfb595313d58b96dd4f9fc0a17b758ff8869a7fd836840370bfe76b94dcad5d195b7455
|
|
7
|
+
data.tar.gz: 257939e10db7512ea4f12d0bd23d8ba472a41c6987ef974cc69c978653491b0e4b107a7e312fb34431745a5b855dae6919a58e8cdd626e8277c9276030a5a6d6
|
data/README.md
CHANGED
|
@@ -125,7 +125,7 @@ bundle exec kamal-backup evidence
|
|
|
125
125
|
|
|
126
126
|
- **Scheduled backups:** the accessory runs continuously and backs up on `backup.schedule`.
|
|
127
127
|
- **Database and Active Storage coverage:** database dumps plus file-backed Active Storage files from mounted volumes.
|
|
128
|
-
- **Restic underneath:** encrypted, deduplicated snapshots in S3-compatible storage, a restic REST server, or a filesystem repository.
|
|
128
|
+
- **Restic underneath:** encrypted, deduplicated snapshots in S3-compatible storage, over SFTP, in a restic REST server, or in a filesystem repository.
|
|
129
129
|
- **Local restores:** inspect production data safely in your local Rails app.
|
|
130
130
|
- **Restore drills:** restore into scratch production-side targets, run verification commands, and record the result.
|
|
131
131
|
- **Security review evidence:** `kamal-backup evidence` prints redacted JSON with latest snapshots, `kamal-backup check` results, drills, retention, and tool versions.
|
data/lib/kamal_backup/app.rb
CHANGED
|
@@ -480,6 +480,10 @@ module KamalBackup
|
|
|
480
480
|
config.validate_restic
|
|
481
481
|
config.validate_database_backup
|
|
482
482
|
config.validate_backup_paths
|
|
483
|
+
# Check writability up front. This used to be discovered only after
|
|
484
|
+
# restic had pulled the entire file snapshot, so a read-only mount cost a
|
|
485
|
+
# full download before failing — the wrong moment to learn about it.
|
|
486
|
+
config.backup_paths.each { |path| ensure_writable_restore_target(path) }
|
|
483
487
|
end
|
|
484
488
|
|
|
485
489
|
def validate_production_drill(file_target, database_name, sqlite_path)
|
|
@@ -34,6 +34,28 @@ module KamalBackup
|
|
|
34
34
|
CommandSpec.new(argv: argv, env: current_connection)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# Replace the target schema before restoring.
|
|
38
|
+
#
|
|
39
|
+
# pg_restore --clean emits DROP TABLE for each object in the dump, but
|
|
40
|
+
# PostgreSQL refuses to drop a table another table still references, and
|
|
41
|
+
# the dump's ordering cannot account for constraints that only exist in
|
|
42
|
+
# the target. Restoring over a schema Rails already created — which is
|
|
43
|
+
# every restore onto a freshly deployed host, since Kamal runs db:prepare
|
|
44
|
+
# on boot — leaves the drops failing, the CREATEs failing as "already
|
|
45
|
+
# exists", the COPYs never running, and the foreign keys rejected because
|
|
46
|
+
# the tables they point at are still empty.
|
|
47
|
+
#
|
|
48
|
+
# Dropping the schema first sidesteps the ordering problem entirely. This
|
|
49
|
+
# only runs for restore-to-current, which already means "replace this
|
|
50
|
+
# database"; scratch restores target a separate database and are
|
|
51
|
+
# untouched.
|
|
52
|
+
def restore_to_current(restic, snapshot, filename)
|
|
53
|
+
reset_current_schema
|
|
54
|
+
result = super
|
|
55
|
+
ensure_restore_reported_no_errors(result)
|
|
56
|
+
result
|
|
57
|
+
end
|
|
58
|
+
|
|
37
59
|
def current_restore_command
|
|
38
60
|
connection = current_connection
|
|
39
61
|
database = connection.fetch('PGDATABASE')
|
|
@@ -61,6 +83,38 @@ module KamalBackup
|
|
|
61
83
|
|
|
62
84
|
private
|
|
63
85
|
|
|
86
|
+
# pg_restore exits 0 even when individual objects fail, reporting only
|
|
87
|
+
# "errors ignored on restore: N" on stderr. A restore that half-applied
|
|
88
|
+
# and claimed success is worse than one that failed, because the damage
|
|
89
|
+
# is only discovered later, from the data.
|
|
90
|
+
def ensure_restore_reported_no_errors(result)
|
|
91
|
+
stderr = result.respond_to?(:stderr) ? result.stderr.to_s : ''
|
|
92
|
+
match = stderr.match(/errors ignored on restore:\s*(\d+)/i)
|
|
93
|
+
return if match.nil? || match[1].to_i.zero?
|
|
94
|
+
|
|
95
|
+
raise CommandError.new(
|
|
96
|
+
"pg_restore reported #{match[1]} ignored error(s); the database is only partially restored. " \
|
|
97
|
+
'Inspect the output above, resolve the cause, and restore again.',
|
|
98
|
+
command: current_restore_command,
|
|
99
|
+
stderr: stderr
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def reset_current_schema
|
|
104
|
+
connection = current_connection
|
|
105
|
+
argv = ['psql', '--quiet', '--no-psqlrc', '--set', 'ON_ERROR_STOP=1', '--command', RESET_SCHEMA_SQL]
|
|
106
|
+
Command.capture(CommandSpec.new(argv: argv, env: connection), redactor: redactor)
|
|
107
|
+
rescue CommandError => e
|
|
108
|
+
raise CommandError.new(
|
|
109
|
+
"failed to reset the public schema before restoring: #{e.message}",
|
|
110
|
+
command: e.command,
|
|
111
|
+
stderr: e.stderr
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
RESET_SCHEMA_SQL = 'DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public;'
|
|
116
|
+
private_constant :RESET_SCHEMA_SQL
|
|
117
|
+
|
|
64
118
|
def validate_scratch_restore_target(target)
|
|
65
119
|
raise ConfigurationError, 'scratch database must differ from the current PostgreSQL database' if current_connection.fetch('PGDATABASE') == target
|
|
66
120
|
|
data/lib/kamal_backup/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kamal-backup
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- crmne
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|