db/migrate/20260523120000_install_backup_tracking.rb
Rama develop · réplica de lectura del repositorio original.
require 'json'
class InstallBackupTracking < ActiveRecord::Migration[8.1]
def up # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
create_table :backups do |t|
t.string :filename, null: false
t.bigint :bytes, null: false
t.datetime :created_at, null: false
t.string :destination, null: false
t.string :external_path
t.string :s3_endpoint_url
t.string :s3_bucket
t.string :s3_prefix
t.string :influxdb_image
t.string :postgresql_image
t.text :files
end
add_index :backups,
%i[destination external_path s3_endpoint_url s3_bucket s3_prefix filename],
unique: true,
name: 'index_backups_on_destination_and_filename'
add_index :backups, %i[destination created_at]
create_table :runner_logs do |t|
t.string :kind, null: false
t.text :last_error_message
t.timestamps
end
add_index :runner_logs, :kind, unique: true
import_legacy_backups_index
end
def down
drop_table :runner_logs
drop_table :backups
end
private
# One-shot import of the pre-SQLite `backups_index.json` cache so users
# upgrading don't lose their list. The JSON is deleted on success.
def import_legacy_backups_index
json_path = File.join(Rails.configuration.data_path, 'helios', 'backups_index.json')
return unless File.exist?(json_path)
parsed = parse_json(json_path)
return unless parsed.is_a?(Hash)
import_backups(parsed)
import_errors(parsed)
File.delete(json_path)
rescue StandardError => e
Rails.logger.warn("InstallBackupTracking legacy import skipped: #{e.message}")
end
def parse_json(path)
JSON.parse(File.read(path))
rescue JSON::ParserError
nil
end
def import_backups(index)
destination = index['destination'].to_s.presence || 'local'
coords = legacy_coords(destination, index)
Array(index['backups']).each do |entry|
next if entry['filename'].blank?
backup_row(destination, coords, entry).save!
end
end
def legacy_coords(destination, index)
case destination
when 'external' then { external_path: index['external_path'] }
when 's3' then { s3_endpoint_url: index['endpoint_url'], s3_bucket: index['bucket'],
s3_prefix: index['prefix'] }
else {}
end
end
def backup_row(destination, coords, entry)
record = Backup.find_or_initialize_by(destination: destination, filename: entry['filename'], **coords)
record.assign_attributes(
bytes: entry['bytes'].to_i,
created_at: parse_time(entry['mtime']) || Time.current,
influxdb_image: entry['influxdb_image'],
postgresql_image: entry['postgresql_image'],
files: Array(entry['files']),
)
record
end
def import_errors(index)
RunnerLog.create!(kind: :backup, last_error_message: index['error_message']) if index['error_message'].present?
return if index['restore_error_message'].blank?
RunnerLog.create!(kind: :restore, last_error_message: index['restore_error_message'])
end
def parse_time(value)
return nil if value.blank?
Time.zone.parse(value.to_s)
rescue ArgumentError
nil
end
end