kamal-backup 0.3.0.beta7 → 0.3.0.beta9
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/lib/kamal_backup/app.rb +59 -1
- data/lib/kamal_backup/cli.rb +24 -6
- data/lib/kamal_backup/command.rb +8 -0
- data/lib/kamal_backup/kamal_bridge.rb +16 -2
- data/lib/kamal_backup/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7c67946461c23a767a12d31d82389bb4e0fb3c75a5494f102c4dad8facecb2e
|
|
4
|
+
data.tar.gz: f0265c16c92bdf291bf19f0586589af08938962c5a6b79635f782559dee75d39
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f919b9df92d55407c57745e61cfa4fd011c15060ab6edd5d93b75516fef13b22f0490e7010531bb6c464578df6a25faf8a485d6a35f5431f9a6e53331cef90a5
|
|
7
|
+
data.tar.gz: d7243e236effb7c4da158c70993af8896e39764952f9dc2155a123655ddac49931804c67dd10424d10a5ddcdd0208542a28fe798256691a4cb2287ef35bba242
|
data/lib/kamal_backup/app.rb
CHANGED
|
@@ -16,6 +16,8 @@ require_relative "schema"
|
|
|
16
16
|
|
|
17
17
|
module KamalBackup
|
|
18
18
|
class App
|
|
19
|
+
FRESH_BACKUP_GRACE_SECONDS = 5
|
|
20
|
+
|
|
19
21
|
attr_reader :config, :redactor
|
|
20
22
|
|
|
21
23
|
def initialize(env: ENV, config: nil, redactor: nil, restic: nil, database: nil, evidence_class: Evidence, scheduler_class: Scheduler)
|
|
@@ -28,6 +30,7 @@ module KamalBackup
|
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
def backup
|
|
33
|
+
started_at = Time.now.utc
|
|
31
34
|
config.validate_backup
|
|
32
35
|
require_restic!
|
|
33
36
|
|
|
@@ -43,7 +46,9 @@ module KamalBackup
|
|
|
43
46
|
restic.check
|
|
44
47
|
end
|
|
45
48
|
|
|
46
|
-
|
|
49
|
+
backup_summary(started_at: started_at, finished_at: Time.now.utc).tap do |summary|
|
|
50
|
+
validate_fresh_backup_summary!(summary, started_at: started_at)
|
|
51
|
+
end
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
def validate(check_files: true)
|
|
@@ -241,6 +246,59 @@ module KamalBackup
|
|
|
241
246
|
result[:files] = file_results.size == 1 ? file_results.first : file_results
|
|
242
247
|
end
|
|
243
248
|
|
|
249
|
+
def backup_summary(started_at:, finished_at:)
|
|
250
|
+
{
|
|
251
|
+
kind: "backup_result",
|
|
252
|
+
status: "ok",
|
|
253
|
+
started_at: started_at.iso8601,
|
|
254
|
+
finished_at: finished_at.iso8601,
|
|
255
|
+
databases: databases.map { |adapter| backup_snapshot_summary(adapter) },
|
|
256
|
+
files: backup_file_snapshot_summary
|
|
257
|
+
}
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def backup_snapshot_summary(adapter)
|
|
261
|
+
snapshot = restic.latest_snapshot(tags: database_snapshot_tags(adapter))
|
|
262
|
+
snapshot_summary(snapshot).merge(
|
|
263
|
+
database: database_config_name(adapter),
|
|
264
|
+
adapter: adapter.adapter_name
|
|
265
|
+
)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def backup_file_snapshot_summary
|
|
269
|
+
return nil if config.backup_paths.empty?
|
|
270
|
+
|
|
271
|
+
snapshot_summary(restic.latest_snapshot(tags: ["type:files"]))
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def snapshot_summary(snapshot)
|
|
275
|
+
{
|
|
276
|
+
snapshot: snapshot && (snapshot["short_id"] || snapshot["id"]),
|
|
277
|
+
time: snapshot && snapshot["time"]
|
|
278
|
+
}
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def validate_fresh_backup_summary!(summary, started_at:)
|
|
282
|
+
stale_databases = summary.fetch(:databases).reject { |entry| fresh_snapshot?(entry, started_at) }
|
|
283
|
+
|
|
284
|
+
unless stale_databases.empty?
|
|
285
|
+
names = stale_databases.map { |entry| entry.fetch(:database) }.join(", ")
|
|
286
|
+
raise ConfigurationError, "backup did not create a fresh database snapshot for #{names}"
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
files = summary[:files]
|
|
290
|
+
if files && !fresh_snapshot?(files, started_at)
|
|
291
|
+
raise ConfigurationError, "backup did not create a fresh file snapshot"
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def fresh_snapshot?(entry, started_at)
|
|
296
|
+
snapshot_time = Time.parse(entry[:time].to_s)
|
|
297
|
+
snapshot_time >= started_at - FRESH_BACKUP_GRACE_SECONDS
|
|
298
|
+
rescue ArgumentError
|
|
299
|
+
false
|
|
300
|
+
end
|
|
301
|
+
|
|
244
302
|
def database_snapshot_tags(adapter)
|
|
245
303
|
["type:database", "database:#{database_config_name(adapter)}", "adapter:#{adapter.adapter_name}"]
|
|
246
304
|
end
|
data/lib/kamal_backup/cli.rb
CHANGED
|
@@ -129,11 +129,26 @@ module KamalBackup
|
|
|
129
129
|
|
|
130
130
|
def print_remote_version_status
|
|
131
131
|
status = remote_version == VERSION ? "in sync" : "out of sync"
|
|
132
|
+
status_color = status == "in sync" ? :green : :red
|
|
133
|
+
status_output = CommandOutput.new(io: $stdout, env: command_env)
|
|
132
134
|
|
|
133
135
|
puts("local: #{VERSION}")
|
|
134
136
|
puts("remote: #{remote_version}")
|
|
135
|
-
puts("status: #{status}")
|
|
136
|
-
puts("fix: #{accessory_reboot_command}") if status == "out of sync"
|
|
137
|
+
puts("status: #{status_output.decorate(status, status_color, :bold)}")
|
|
138
|
+
puts("fix: #{status_output.decorate(accessory_reboot_command, :yellow, :bold)}") if status == "out of sync"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def print_backup_result(result)
|
|
142
|
+
return unless result.is_a?(Hash)
|
|
143
|
+
|
|
144
|
+
puts("Backup completed at #{result.fetch(:finished_at)}")
|
|
145
|
+
result.fetch(:databases).each do |database|
|
|
146
|
+
puts("database #{database.fetch(:database)}: #{database.fetch(:snapshot)} at #{database.fetch(:time)}")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
if files = result[:files]
|
|
150
|
+
puts("files: #{files.fetch(:snapshot)} at #{files.fetch(:time)}")
|
|
151
|
+
end
|
|
137
152
|
end
|
|
138
153
|
|
|
139
154
|
def validate_deploy_config
|
|
@@ -399,14 +414,17 @@ module KamalBackup
|
|
|
399
414
|
|
|
400
415
|
def self.start(argv = ARGV, env: ENV)
|
|
401
416
|
self.command_env = env
|
|
402
|
-
|
|
417
|
+
output = CommandOutput.new(io: $stderr, env: env)
|
|
418
|
+
Command.with_output(output) do
|
|
403
419
|
super(normalize_global_options(argv))
|
|
404
420
|
end
|
|
405
421
|
rescue Error => e
|
|
406
|
-
|
|
422
|
+
output ||= CommandOutput.new(io: $stderr, env: env)
|
|
423
|
+
output.error("(#{e.class}): #{e.message}", redactor: Redactor.new(env: env))
|
|
407
424
|
exit(1)
|
|
408
425
|
rescue Interrupt
|
|
409
|
-
|
|
426
|
+
output ||= CommandOutput.new(io: $stderr, env: env)
|
|
427
|
+
output.error("(Interrupt): interrupted", redactor: Redactor.new(env: env))
|
|
410
428
|
exit(130)
|
|
411
429
|
ensure
|
|
412
430
|
self.command_env = nil
|
|
@@ -419,7 +437,7 @@ module KamalBackup
|
|
|
419
437
|
if remote_command_mode?
|
|
420
438
|
exec_remote(["kamal-backup", "backup"])
|
|
421
439
|
else
|
|
422
|
-
direct_app.backup
|
|
440
|
+
print_backup_result(direct_app.backup)
|
|
423
441
|
end
|
|
424
442
|
end
|
|
425
443
|
|
data/lib/kamal_backup/command.rb
CHANGED
|
@@ -72,6 +72,14 @@ module KamalBackup
|
|
|
72
72
|
write_message("INFO", redactor.redact_string(message))
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
def error(message, redactor:)
|
|
76
|
+
write_message("ERROR", colorize(redactor.redact_string(message), :red, :bold))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def decorate(value, color, mode = nil)
|
|
80
|
+
colorize(value, color, mode)
|
|
81
|
+
end
|
|
82
|
+
|
|
75
83
|
def command_start(spec, redactor:)
|
|
76
84
|
id = SecureRandom.hex(4)
|
|
77
85
|
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
@@ -215,10 +215,10 @@ module KamalBackup
|
|
|
215
215
|
end
|
|
216
216
|
|
|
217
217
|
def capture_kamal(argv, stream: false)
|
|
218
|
-
spec = CommandSpec.new(argv: argv)
|
|
218
|
+
spec = CommandSpec.new(argv: argv, env: kamal_stream_env(stream))
|
|
219
219
|
options = {
|
|
220
220
|
redactor: @redactor,
|
|
221
|
-
log:
|
|
221
|
+
log: !stream,
|
|
222
222
|
log_output: false,
|
|
223
223
|
tee_stdout: stream ? @stdout : nil,
|
|
224
224
|
tee_stderr: stream ? @stderr : nil
|
|
@@ -231,6 +231,20 @@ module KamalBackup
|
|
|
231
231
|
end
|
|
232
232
|
end
|
|
233
233
|
|
|
234
|
+
def kamal_stream_env(stream)
|
|
235
|
+
return {} unless stream
|
|
236
|
+
|
|
237
|
+
if @env["SSHKIT_COLOR"].to_s.empty?
|
|
238
|
+
stream_color? ? { "SSHKIT_COLOR" => "1" } : {}
|
|
239
|
+
else
|
|
240
|
+
{ "SSHKIT_COLOR" => @env["SSHKIT_COLOR"] }
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def stream_color?
|
|
245
|
+
[@stdout, @stderr].any? { |io| io.respond_to?(:tty?) && io.tty? }
|
|
246
|
+
end
|
|
247
|
+
|
|
234
248
|
def parse_version_line(output)
|
|
235
249
|
output.to_s.lines.map(&:strip).reverse.find { |line| line.match?(VERSION_LINE_PATTERN) }.to_s
|
|
236
250
|
end
|
data/lib/kamal_backup/version.rb
CHANGED