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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 841b29d2ee8cc4598cb1880da4a7bf884cef141816432a9d960ff400b7535d25
4
- data.tar.gz: 3784f1bfe50f1480f457bfb40a18e30ab5905db4a300a193fe5d68ac50d67f2f
3
+ metadata.gz: c7c67946461c23a767a12d31d82389bb4e0fb3c75a5494f102c4dad8facecb2e
4
+ data.tar.gz: f0265c16c92bdf291bf19f0586589af08938962c5a6b79635f782559dee75d39
5
5
  SHA512:
6
- metadata.gz: 9929349441963b8297254c10faa1446cf4f7602638a7637ab655ffcadb32d31d012ea8827890fc29175dfc4e344a5afc1edacbf960cfe86d6aec2a83c88385c6
7
- data.tar.gz: fbfee00eae64424a92e5d039512ff51d01abecb6dd6a61d8d44ec97dc2e86208f06fd5c191947107a9c9adaa4da80a77a9eebabc9e9e12b96d7e60dcb0ce0e06
6
+ metadata.gz: f919b9df92d55407c57745e61cfa4fd011c15060ab6edd5d93b75516fef13b22f0490e7010531bb6c464578df6a25faf8a485d6a35f5431f9a6e53331cef90a5
7
+ data.tar.gz: d7243e236effb7c4da158c70993af8896e39764952f9dc2155a123655ddac49931804c67dd10424d10a5ddcdd0208542a28fe798256691a4cb2287ef35bba242
@@ -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
- true
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
@@ -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
- Command.with_output(CommandOutput.new(io: $stderr)) do
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
- warn("kamal-backup: #{Redactor.new(env: env).redact_string(e.message)}")
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
- warn("kamal-backup: interrupted")
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
 
@@ -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: false,
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
@@ -1,3 +1,3 @@
1
1
  module KamalBackup
2
- VERSION = "0.3.0.beta7"
2
+ VERSION = "0.3.0.beta9"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamal-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.beta7
4
+ version: 0.3.0.beta9
5
5
  platform: ruby
6
6
  authors:
7
7
  - crmne