kamal-backup 0.3.0.beta9 → 0.3.0.beta10

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: c7c67946461c23a767a12d31d82389bb4e0fb3c75a5494f102c4dad8facecb2e
4
- data.tar.gz: f0265c16c92bdf291bf19f0586589af08938962c5a6b79635f782559dee75d39
3
+ metadata.gz: c534b081ef41f75a8d10e72ae0fcbaa7b81ad5a0f2cbb3a12c281353d74ab69b
4
+ data.tar.gz: 6bd3b48d5573ab6a1e19ec1452584b7d44fc62e852edb7c202e2191d3bac6743
5
5
  SHA512:
6
- metadata.gz: f919b9df92d55407c57745e61cfa4fd011c15060ab6edd5d93b75516fef13b22f0490e7010531bb6c464578df6a25faf8a485d6a35f5431f9a6e53331cef90a5
7
- data.tar.gz: d7243e236effb7c4da158c70993af8896e39764952f9dc2155a123655ddac49931804c67dd10424d10a5ddcdd0208542a28fe798256691a4cb2287ef35bba242
6
+ metadata.gz: fdef6636e464308da24e9bb11aa64d8e68535fc9a8fc261a4dce02dc7e0d0bd26f20320f9dfede598085d780764310b0b3a7ad71ba3fe8d48e61d5c756b3597a
7
+ data.tar.gz: de0667fae6cb0ae276f1b4511ea1c63ffe8655523648a0e3f1e40d6872e53ba2823b4d987d4e651ace4d47710d516d17dc32f9e269e9bd20115501a533f793be
data/README.md CHANGED
@@ -112,6 +112,8 @@ bundle exec kamal-backup check
112
112
  bundle exec kamal-backup evidence
113
113
  ```
114
114
 
115
+ `backup` respects `backup.schedule` and skips when the latest backup is still current. Use `bundle exec kamal-backup backup --force` when you deliberately want an immediate snapshot.
116
+
115
117
  ## What you get
116
118
 
117
119
  - **Scheduled backups:** the accessory runs continuously and backs up on `backup.schedule`.
@@ -29,9 +29,11 @@ module KamalBackup
29
29
  @scheduler_class = scheduler_class
30
30
  end
31
31
 
32
- def backup
32
+ def backup(force: false)
33
33
  started_at = Time.now.utc
34
34
  config.validate_backup
35
+ return skipped_backup_result(started_at) unless force || backup_due?(started_at)
36
+
35
37
  require_restic!
36
38
 
37
39
  restic.ensure_repository
@@ -48,6 +50,7 @@ module KamalBackup
48
50
 
49
51
  backup_summary(started_at: started_at, finished_at: Time.now.utc).tap do |summary|
50
52
  validate_fresh_backup_summary!(summary, started_at: started_at)
53
+ write_last_backup(summary)
51
54
  end
52
55
  end
53
56
 
@@ -135,10 +138,48 @@ module KamalBackup
135
138
 
136
139
  def schedule
137
140
  config.validate_backup
138
- @scheduler_class.new(config) { backup }.run
141
+ @scheduler_class.new(config) { backup(force: true) }.run
139
142
  end
140
143
 
141
144
  private
145
+ def backup_due?(now)
146
+ due_at = next_backup_at
147
+ due_at.nil? || now >= due_at
148
+ end
149
+
150
+ def skipped_backup_result(now)
151
+ {
152
+ kind: "backup_result",
153
+ status: "skipped",
154
+ reason: "not_due",
155
+ last_backup_at: last_backup_finished_at&.iso8601,
156
+ next_backup_at: next_backup_at&.iso8601,
157
+ force_command: "kamal-backup backup --force",
158
+ finished_at: now.iso8601
159
+ }
160
+ end
161
+
162
+ def next_backup_at
163
+ last_backup_finished_at + config.backup_schedule_seconds if last_backup_finished_at
164
+ end
165
+
166
+ def last_backup_finished_at
167
+ @last_backup_finished_at ||= begin
168
+ value = last_backup_record["finished_at"] || last_backup_record["last_backup_at"]
169
+ value ? Time.parse(value.to_s).utc : nil
170
+ rescue ArgumentError
171
+ nil
172
+ end
173
+ end
174
+
175
+ def last_backup_record
176
+ @last_backup_record ||= begin
177
+ JSON.parse(File.read(config.last_backup_path))
178
+ rescue JSON::ParserError, SystemCallError
179
+ {}
180
+ end
181
+ end
182
+
142
183
  def build_restore_result(scope, snapshot)
143
184
  started_at = Time.now.utc
144
185
  result = Schema.record(
@@ -299,6 +340,15 @@ module KamalBackup
299
340
  false
300
341
  end
301
342
 
343
+ def write_last_backup(result)
344
+ FileUtils.mkdir_p(config.state_dir)
345
+ File.write(config.last_backup_path, JSON.pretty_generate(result))
346
+ @last_backup_record = result.transform_keys(&:to_s)
347
+ @last_backup_finished_at = Time.parse(result.fetch(:finished_at)).utc
348
+ rescue SystemCallError, ArgumentError
349
+ nil
350
+ end
351
+
302
352
  def database_snapshot_tags(adapter)
303
353
  ["type:database", "database:#{database_config_name(adapter)}", "adapter:#{adapter.adapter_name}"]
304
354
  end
@@ -141,6 +141,13 @@ module KamalBackup
141
141
  def print_backup_result(result)
142
142
  return unless result.is_a?(Hash)
143
143
 
144
+ if result[:status] == "skipped"
145
+ puts("No backup due. Last backup finished at #{result.fetch(:last_backup_at)}.")
146
+ puts("Next backup is due at #{result.fetch(:next_backup_at)}.")
147
+ puts("Run `#{result.fetch(:force_command)}` to force a backup now.")
148
+ return
149
+ end
150
+
144
151
  puts("Backup completed at #{result.fetch(:finished_at)}")
145
152
  result.fetch(:databases).each do |database|
146
153
  puts("database #{database.fetch(:database)}: #{database.fetch(:snapshot)} at #{database.fetch(:time)}")
@@ -432,12 +439,15 @@ module KamalBackup
432
439
 
433
440
  include Helpers
434
441
 
435
- desc "backup", "Run one database and Active Storage backup immediately"
442
+ method_option :force, type: :boolean, default: false, desc: "Run a backup even if the configured schedule is not due"
443
+ desc "backup", "Run a due database and Active Storage backup"
436
444
  def backup
437
445
  if remote_command_mode?
438
- exec_remote(["kamal-backup", "backup"])
446
+ argv = ["kamal-backup", "backup"]
447
+ argv << "--force" if options[:force]
448
+ exec_remote(argv)
439
449
  else
440
- print_backup_result(direct_app.backup)
450
+ print_backup_result(direct_app.backup(force: options[:force]))
441
451
  end
442
452
  end
443
453
 
@@ -215,6 +215,10 @@ module KamalBackup
215
215
  File.join(state_dir, "last_check.json")
216
216
  end
217
217
 
218
+ def last_backup_path
219
+ File.join(state_dir, "last_backup.json")
220
+ end
221
+
218
222
  def last_restore_drill_path
219
223
  File.join(state_dir, "last_restore_drill.json")
220
224
  end
@@ -1,3 +1,3 @@
1
1
  module KamalBackup
2
- VERSION = "0.3.0.beta9"
2
+ VERSION = "0.3.0.beta10"
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.beta9
4
+ version: 0.3.0.beta10
5
5
  platform: ruby
6
6
  authors:
7
7
  - crmne