archive_storage 0.1.0
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 +7 -0
- data/README.md +348 -0
- data/archive_storage.gemspec +42 -0
- data/lib/archive_storage/adapters/filesystem.rb +135 -0
- data/lib/archive_storage/adapters/memory.rb +101 -0
- data/lib/archive_storage/adapters/metadata.rb +23 -0
- data/lib/archive_storage/adapters/s3.rb +186 -0
- data/lib/archive_storage/configuration.rb +115 -0
- data/lib/archive_storage/duration_parser.rb +26 -0
- data/lib/archive_storage/enqueuer.rb +29 -0
- data/lib/archive_storage/errors.rb +9 -0
- data/lib/archive_storage/jobs/migration_job.rb +28 -0
- data/lib/archive_storage/jobs/queue_job.rb +65 -0
- data/lib/archive_storage/jobs/sidekiq_migration_worker.rb +28 -0
- data/lib/archive_storage/jobs/sidekiq_queue_worker.rb +65 -0
- data/lib/archive_storage/migration_rate.rb +16 -0
- data/lib/archive_storage/migrator.rb +151 -0
- data/lib/archive_storage/model.rb +35 -0
- data/lib/archive_storage/models/file_record.rb +26 -0
- data/lib/archive_storage/mount_config.rb +50 -0
- data/lib/archive_storage/plan_result.rb +61 -0
- data/lib/archive_storage/planner.rb +190 -0
- data/lib/archive_storage/policy.rb +48 -0
- data/lib/archive_storage/policy_builder.rb +72 -0
- data/lib/archive_storage/railtie.rb +23 -0
- data/lib/archive_storage/registry.rb +109 -0
- data/lib/archive_storage/schedule_config.rb +79 -0
- data/lib/archive_storage/scheduler.rb +93 -0
- data/lib/archive_storage/storage.rb +91 -0
- data/lib/archive_storage/storage_config.rb +37 -0
- data/lib/archive_storage/storage_rule.rb +57 -0
- data/lib/archive_storage/stored_file.rb +94 -0
- data/lib/archive_storage/tasks.rake +82 -0
- data/lib/archive_storage/verification_result.rb +11 -0
- data/lib/archive_storage/verifier.rb +144 -0
- data/lib/archive_storage/version.rb +5 -0
- data/lib/archive_storage.rb +148 -0
- data/lib/generators/archive_storage/install_generator.rb +28 -0
- data/lib/generators/archive_storage/templates/create_archive_storage_files.rb +53 -0
- metadata +227 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "errors"
|
|
4
|
+
require_relative "adapters/metadata"
|
|
5
|
+
require_relative "verification_result"
|
|
6
|
+
|
|
7
|
+
module ArchiveStorage
|
|
8
|
+
class Verifier
|
|
9
|
+
def initialize(strategy: ArchiveStorage.configuration.verification_strategy)
|
|
10
|
+
@strategy = strategy.to_sym
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def verify!(source_adapter:, target_adapter:, source_key:, target_key:)
|
|
14
|
+
source_metadata = source_adapter.head(source_key)
|
|
15
|
+
target_metadata = target_adapter.head(target_key)
|
|
16
|
+
|
|
17
|
+
verify_metadata!(source_metadata, target_metadata)
|
|
18
|
+
verify_bytes!(source_adapter, target_adapter, source_key, target_key) if strategy == :byte_compare
|
|
19
|
+
|
|
20
|
+
VerificationResult.new(
|
|
21
|
+
strategy: strategy,
|
|
22
|
+
matched_by: matched_by(source_metadata, target_metadata),
|
|
23
|
+
source_metadata: source_metadata,
|
|
24
|
+
target_metadata: target_metadata
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
attr_reader :strategy
|
|
31
|
+
|
|
32
|
+
def verify_metadata!(source_metadata, target_metadata)
|
|
33
|
+
verify_size!(source_metadata, target_metadata)
|
|
34
|
+
|
|
35
|
+
case strategy
|
|
36
|
+
when :auto
|
|
37
|
+
verify_auto!(source_metadata, target_metadata)
|
|
38
|
+
when :size
|
|
39
|
+
true
|
|
40
|
+
when :etag
|
|
41
|
+
verify_etag!(source_metadata, target_metadata, allow_multipart: true)
|
|
42
|
+
when :safe_etag
|
|
43
|
+
verify_safe_etag!(source_metadata, target_metadata)
|
|
44
|
+
when :checksum
|
|
45
|
+
verify_checksum!(source_metadata, target_metadata)
|
|
46
|
+
when :byte_compare
|
|
47
|
+
true
|
|
48
|
+
else
|
|
49
|
+
raise ConfigurationError, "unknown verification strategy #{strategy.inspect}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def verify_size!(source_metadata, target_metadata)
|
|
54
|
+
return if source_metadata.byte_size == target_metadata.byte_size
|
|
55
|
+
|
|
56
|
+
raise VerificationError,
|
|
57
|
+
"byte size mismatch: #{source_metadata.byte_size} != #{target_metadata.byte_size}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def verify_auto!(source_metadata, target_metadata)
|
|
61
|
+
if comparable_checksums?(source_metadata, target_metadata)
|
|
62
|
+
verify_checksum!(source_metadata, target_metadata)
|
|
63
|
+
elsif comparable_safe_etags?(source_metadata, target_metadata)
|
|
64
|
+
verify_safe_etag!(source_metadata, target_metadata)
|
|
65
|
+
else
|
|
66
|
+
true
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def verify_checksum!(source_metadata, target_metadata)
|
|
71
|
+
unless comparable_checksums?(source_metadata, target_metadata)
|
|
72
|
+
raise VerificationError, "checksums are not available or use different algorithms"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
return if source_metadata.checksum == target_metadata.checksum
|
|
76
|
+
|
|
77
|
+
raise VerificationError,
|
|
78
|
+
"checksum mismatch: #{source_metadata.checksum.inspect} != #{target_metadata.checksum.inspect}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def verify_safe_etag!(source_metadata, target_metadata)
|
|
82
|
+
unless comparable_safe_etags?(source_metadata, target_metadata)
|
|
83
|
+
raise VerificationError, "safe etags are not available"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
verify_etag!(source_metadata, target_metadata, allow_multipart: false)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def verify_etag!(source_metadata, target_metadata, allow_multipart:)
|
|
90
|
+
unless source_metadata.etag && target_metadata.etag
|
|
91
|
+
raise VerificationError, "etags are not available"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if !allow_multipart && (source_metadata.multipart_etag? || target_metadata.multipart_etag?)
|
|
95
|
+
raise VerificationError, "multipart etags are not stable content checksums"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
return if source_metadata.etag == target_metadata.etag
|
|
99
|
+
|
|
100
|
+
raise VerificationError,
|
|
101
|
+
"etag mismatch: #{source_metadata.etag.inspect} != #{target_metadata.etag.inspect}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def verify_bytes!(source_adapter, target_adapter, source_key, target_key)
|
|
105
|
+
source_body = source_adapter.read(source_key)
|
|
106
|
+
target_body = target_adapter.read(target_key)
|
|
107
|
+
return if source_body == target_body
|
|
108
|
+
|
|
109
|
+
raise VerificationError, "byte comparison mismatch"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def comparable_checksums?(source_metadata, target_metadata)
|
|
113
|
+
source_metadata.checksum &&
|
|
114
|
+
target_metadata.checksum &&
|
|
115
|
+
source_metadata.checksum_algorithm &&
|
|
116
|
+
source_metadata.checksum_algorithm == target_metadata.checksum_algorithm
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def comparable_safe_etags?(source_metadata, target_metadata)
|
|
120
|
+
source_metadata.safe_etag? &&
|
|
121
|
+
target_metadata.safe_etag?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def matched_by(source_metadata, target_metadata)
|
|
125
|
+
case strategy
|
|
126
|
+
when :checksum
|
|
127
|
+
:checksum
|
|
128
|
+
when :etag
|
|
129
|
+
:etag
|
|
130
|
+
when :safe_etag
|
|
131
|
+
:safe_etag
|
|
132
|
+
when :byte_compare
|
|
133
|
+
:byte_compare
|
|
134
|
+
when :auto
|
|
135
|
+
return :checksum if comparable_checksums?(source_metadata, target_metadata)
|
|
136
|
+
return :safe_etag if comparable_safe_etags?(source_metadata, target_metadata)
|
|
137
|
+
|
|
138
|
+
:size
|
|
139
|
+
else
|
|
140
|
+
:size
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "carrierwave"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# ArchiveStorage can be loaded without CarrierWave. CarrierWave integration is optional at runtime.
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require_relative "archive_storage/version"
|
|
10
|
+
require_relative "archive_storage/errors"
|
|
11
|
+
require_relative "archive_storage/configuration"
|
|
12
|
+
require_relative "archive_storage/policy_builder"
|
|
13
|
+
require_relative "archive_storage/model"
|
|
14
|
+
require_relative "archive_storage/registry"
|
|
15
|
+
require_relative "archive_storage/storage"
|
|
16
|
+
require_relative "archive_storage/planner"
|
|
17
|
+
require_relative "archive_storage/enqueuer"
|
|
18
|
+
require_relative "archive_storage/verifier"
|
|
19
|
+
require_relative "archive_storage/migrator"
|
|
20
|
+
require_relative "archive_storage/jobs/queue_job"
|
|
21
|
+
require_relative "archive_storage/jobs/migration_job"
|
|
22
|
+
|
|
23
|
+
module ArchiveStorage
|
|
24
|
+
class << self
|
|
25
|
+
attr_writer :configuration, :registry
|
|
26
|
+
|
|
27
|
+
def configuration
|
|
28
|
+
@configuration ||= Configuration.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def configure
|
|
32
|
+
yield configuration
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def reset_configuration!
|
|
36
|
+
@configuration = Configuration.new
|
|
37
|
+
@registry = Registry.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def adapter(name)
|
|
41
|
+
configuration.adapter(name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def registry
|
|
45
|
+
@registry ||= Registry.new
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def register_uploader(uploader_class)
|
|
49
|
+
uploaders << uploader_class
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def register_mount(model, mounted_as, uploader:, policy:)
|
|
53
|
+
configuration.mount(model, mounted_as, uploader: uploader, policy: policy)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def wire_carrierwave_uploader!(uploader_class)
|
|
57
|
+
return unless uploader_class
|
|
58
|
+
|
|
59
|
+
uploader_class.include(CarrierWave) unless uploader_class < CarrierWave
|
|
60
|
+
uploader_class.storage(:archive_storage) if uploader_class.respond_to?(:storage)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def policy_for_uploader(uploader)
|
|
64
|
+
mount_policy_for_uploader(uploader) ||
|
|
65
|
+
(uploader.class.archive_storage_policy if uploader.class.respond_to?(:archive_storage_policy))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def policy_for_mount(model, mounted_as)
|
|
69
|
+
configuration.find_mount(model, mounted_as)&.policy ||
|
|
70
|
+
model_policy(model, mounted_as)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def policy_for_record(record_type, mounted_as)
|
|
74
|
+
policy_for_mount(record_type, mounted_as)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def uploaders
|
|
78
|
+
@uploaders ||= []
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def good_job_cron
|
|
82
|
+
configuration.schedules.each_with_object({}) do |schedule, entries|
|
|
83
|
+
entries[schedule.entry_name] = schedule.good_job_entry
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def sidekiq_cron
|
|
88
|
+
configuration.schedules.each_with_object({}) do |schedule, entries|
|
|
89
|
+
entries[schedule.entry_name.to_s] = schedule.sidekiq_cron_entry
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def model_policy(model, mounted_as)
|
|
96
|
+
model_class = model.is_a?(Class) ? model : constantize(model)
|
|
97
|
+
return nil unless model_class.respond_to?(:archive_storage_policy_for)
|
|
98
|
+
|
|
99
|
+
model_class.archive_storage_policy_for(mounted_as)
|
|
100
|
+
rescue NameError
|
|
101
|
+
nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def mount_policy_for_uploader(uploader)
|
|
105
|
+
return nil unless uploader.respond_to?(:model) && uploader.model
|
|
106
|
+
return nil unless uploader.respond_to?(:mounted_as) && uploader.mounted_as
|
|
107
|
+
|
|
108
|
+
policy_for_mount(uploader.model.class, uploader.mounted_as)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def constantize(value)
|
|
112
|
+
value.to_s.split("::").inject(Object) { |namespace, name| namespace.const_get(name) }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
module CarrierWave
|
|
117
|
+
def self.included(base)
|
|
118
|
+
ArchiveStorage.register_uploader(base)
|
|
119
|
+
base.extend(ClassMethods)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
module ClassMethods
|
|
123
|
+
def archive_storage(&block)
|
|
124
|
+
if block
|
|
125
|
+
@archive_storage_policy = PolicyBuilder.build(&block)
|
|
126
|
+
else
|
|
127
|
+
archive_storage_policy
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def archive_storage_policy
|
|
132
|
+
@archive_storage_policy ||
|
|
133
|
+
(superclass.archive_storage_policy if superclass.respond_to?(:archive_storage_policy))
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
if defined?(::CarrierWave) && ::CarrierWave.respond_to?(:configure)
|
|
140
|
+
::CarrierWave.configure do |config|
|
|
141
|
+
if config.respond_to?(:storage_engines)
|
|
142
|
+
config.storage_engines[:archive_storage] = "ArchiveStorage::Storage"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
require_relative "archive_storage/scheduler"
|
|
148
|
+
require_relative "archive_storage/railtie" if defined?(::Rails::Railtie)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module ArchiveStorage
|
|
7
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
8
|
+
include ::Rails::Generators::Migration
|
|
9
|
+
namespace "archive_storage:install"
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
|
|
13
|
+
def copy_migration
|
|
14
|
+
migration_template(
|
|
15
|
+
"create_archive_storage_files.rb",
|
|
16
|
+
"db/migrate/create_archive_storage_files.rb"
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.next_migration_number(dirname)
|
|
21
|
+
if ::ActiveRecord::Base.timestamped_migrations
|
|
22
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
23
|
+
else
|
|
24
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateArchiveStorageFiles < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
create_table :archive_storage_files do |t|
|
|
6
|
+
t.string :record_type, null: false
|
|
7
|
+
t.bigint :record_id, null: false
|
|
8
|
+
t.string :mounted_as, null: false
|
|
9
|
+
t.string :uploader, null: false
|
|
10
|
+
|
|
11
|
+
t.string :identifier, null: false
|
|
12
|
+
t.string :storage_key, null: false
|
|
13
|
+
t.string :source_storage_key
|
|
14
|
+
t.string :target_storage_key
|
|
15
|
+
|
|
16
|
+
t.string :current_storage, null: false
|
|
17
|
+
t.string :source_storage
|
|
18
|
+
t.string :target_storage
|
|
19
|
+
|
|
20
|
+
t.bigint :byte_size
|
|
21
|
+
t.string :checksum
|
|
22
|
+
t.string :content_type
|
|
23
|
+
|
|
24
|
+
t.datetime :migration_started_at
|
|
25
|
+
t.datetime :enqueued_at
|
|
26
|
+
t.datetime :migrated_at
|
|
27
|
+
t.datetime :verified_at
|
|
28
|
+
t.datetime :source_deleted_at
|
|
29
|
+
|
|
30
|
+
t.boolean :source_delete_pending, null: false, default: false
|
|
31
|
+
t.string :last_error
|
|
32
|
+
t.integer :attempts, null: false, default: 0
|
|
33
|
+
|
|
34
|
+
t.timestamps
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
add_index :archive_storage_files,
|
|
38
|
+
[:record_type, :record_id, :mounted_as, :identifier],
|
|
39
|
+
name: "idx_archive_storage_identity"
|
|
40
|
+
|
|
41
|
+
add_index :archive_storage_files,
|
|
42
|
+
[:uploader, :current_storage],
|
|
43
|
+
name: "idx_archive_storage_uploader_storage"
|
|
44
|
+
|
|
45
|
+
add_index :archive_storage_files,
|
|
46
|
+
[:source_storage, :source_deleted_at],
|
|
47
|
+
name: "idx_archive_storage_cleanup"
|
|
48
|
+
|
|
49
|
+
add_index :archive_storage_files,
|
|
50
|
+
[:source_delete_pending, :source_deleted_at],
|
|
51
|
+
name: "idx_archive_storage_delete_pending"
|
|
52
|
+
end
|
|
53
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: archive_storage
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- E. Tashkovyan
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-26 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activejob
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.1'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '9.0'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '6.1'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '9.0'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: activerecord
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '6.1'
|
|
40
|
+
- - "<"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '9.0'
|
|
43
|
+
type: :runtime
|
|
44
|
+
prerelease: false
|
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '6.1'
|
|
50
|
+
- - "<"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '9.0'
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: activesupport
|
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '6.1'
|
|
60
|
+
- - "<"
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '9.0'
|
|
63
|
+
type: :runtime
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '6.1'
|
|
70
|
+
- - "<"
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '9.0'
|
|
73
|
+
- !ruby/object:Gem::Dependency
|
|
74
|
+
name: railties
|
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: '6.1'
|
|
80
|
+
- - "<"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '9.0'
|
|
83
|
+
type: :runtime
|
|
84
|
+
prerelease: false
|
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '6.1'
|
|
90
|
+
- - "<"
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '9.0'
|
|
93
|
+
- !ruby/object:Gem::Dependency
|
|
94
|
+
name: aws-sdk-s3
|
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - "~>"
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: '1'
|
|
100
|
+
type: :development
|
|
101
|
+
prerelease: false
|
|
102
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
103
|
+
requirements:
|
|
104
|
+
- - "~>"
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: '1'
|
|
107
|
+
- !ruby/object:Gem::Dependency
|
|
108
|
+
name: carrierwave
|
|
109
|
+
requirement: !ruby/object:Gem::Requirement
|
|
110
|
+
requirements:
|
|
111
|
+
- - ">="
|
|
112
|
+
- !ruby/object:Gem::Version
|
|
113
|
+
version: '2.2'
|
|
114
|
+
- - "<"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '4.0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '2.2'
|
|
124
|
+
- - "<"
|
|
125
|
+
- !ruby/object:Gem::Version
|
|
126
|
+
version: '4.0'
|
|
127
|
+
- !ruby/object:Gem::Dependency
|
|
128
|
+
name: minitest
|
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
|
130
|
+
requirements:
|
|
131
|
+
- - "~>"
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '5.0'
|
|
134
|
+
type: :development
|
|
135
|
+
prerelease: false
|
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
137
|
+
requirements:
|
|
138
|
+
- - "~>"
|
|
139
|
+
- !ruby/object:Gem::Version
|
|
140
|
+
version: '5.0'
|
|
141
|
+
- !ruby/object:Gem::Dependency
|
|
142
|
+
name: rake
|
|
143
|
+
requirement: !ruby/object:Gem::Requirement
|
|
144
|
+
requirements:
|
|
145
|
+
- - "~>"
|
|
146
|
+
- !ruby/object:Gem::Version
|
|
147
|
+
version: '13.0'
|
|
148
|
+
type: :development
|
|
149
|
+
prerelease: false
|
|
150
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
151
|
+
requirements:
|
|
152
|
+
- - "~>"
|
|
153
|
+
- !ruby/object:Gem::Version
|
|
154
|
+
version: '13.0'
|
|
155
|
+
description: Move uploads across storage backends such as filesystem, NFS, MinIO,
|
|
156
|
+
and S3 without downtime.
|
|
157
|
+
email: []
|
|
158
|
+
executables: []
|
|
159
|
+
extensions: []
|
|
160
|
+
extra_rdoc_files: []
|
|
161
|
+
files:
|
|
162
|
+
- README.md
|
|
163
|
+
- archive_storage.gemspec
|
|
164
|
+
- lib/archive_storage.rb
|
|
165
|
+
- lib/archive_storage/adapters/filesystem.rb
|
|
166
|
+
- lib/archive_storage/adapters/memory.rb
|
|
167
|
+
- lib/archive_storage/adapters/metadata.rb
|
|
168
|
+
- lib/archive_storage/adapters/s3.rb
|
|
169
|
+
- lib/archive_storage/configuration.rb
|
|
170
|
+
- lib/archive_storage/duration_parser.rb
|
|
171
|
+
- lib/archive_storage/enqueuer.rb
|
|
172
|
+
- lib/archive_storage/errors.rb
|
|
173
|
+
- lib/archive_storage/jobs/migration_job.rb
|
|
174
|
+
- lib/archive_storage/jobs/queue_job.rb
|
|
175
|
+
- lib/archive_storage/jobs/sidekiq_migration_worker.rb
|
|
176
|
+
- lib/archive_storage/jobs/sidekiq_queue_worker.rb
|
|
177
|
+
- lib/archive_storage/migration_rate.rb
|
|
178
|
+
- lib/archive_storage/migrator.rb
|
|
179
|
+
- lib/archive_storage/model.rb
|
|
180
|
+
- lib/archive_storage/models/file_record.rb
|
|
181
|
+
- lib/archive_storage/mount_config.rb
|
|
182
|
+
- lib/archive_storage/plan_result.rb
|
|
183
|
+
- lib/archive_storage/planner.rb
|
|
184
|
+
- lib/archive_storage/policy.rb
|
|
185
|
+
- lib/archive_storage/policy_builder.rb
|
|
186
|
+
- lib/archive_storage/railtie.rb
|
|
187
|
+
- lib/archive_storage/registry.rb
|
|
188
|
+
- lib/archive_storage/schedule_config.rb
|
|
189
|
+
- lib/archive_storage/scheduler.rb
|
|
190
|
+
- lib/archive_storage/storage.rb
|
|
191
|
+
- lib/archive_storage/storage_config.rb
|
|
192
|
+
- lib/archive_storage/storage_rule.rb
|
|
193
|
+
- lib/archive_storage/stored_file.rb
|
|
194
|
+
- lib/archive_storage/tasks.rake
|
|
195
|
+
- lib/archive_storage/verification_result.rb
|
|
196
|
+
- lib/archive_storage/verifier.rb
|
|
197
|
+
- lib/archive_storage/version.rb
|
|
198
|
+
- lib/generators/archive_storage/install_generator.rb
|
|
199
|
+
- lib/generators/archive_storage/templates/create_archive_storage_files.rb
|
|
200
|
+
homepage: https://github.com/estashkovyan/archive_storage
|
|
201
|
+
licenses:
|
|
202
|
+
- MIT
|
|
203
|
+
metadata:
|
|
204
|
+
allowed_push_host: https://rubygems.org
|
|
205
|
+
homepage_uri: https://github.com/estashkovyan/archive_storage
|
|
206
|
+
source_code_uri: https://github.com/estashkovyan/archive_storage/tree/main
|
|
207
|
+
changelog_uri: https://github.com/estashkovyan/archive_storage/releases
|
|
208
|
+
post_install_message:
|
|
209
|
+
rdoc_options: []
|
|
210
|
+
require_paths:
|
|
211
|
+
- lib
|
|
212
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
213
|
+
requirements:
|
|
214
|
+
- - ">="
|
|
215
|
+
- !ruby/object:Gem::Version
|
|
216
|
+
version: 3.1.0
|
|
217
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
|
+
requirements:
|
|
219
|
+
- - ">="
|
|
220
|
+
- !ruby/object:Gem::Version
|
|
221
|
+
version: '0'
|
|
222
|
+
requirements: []
|
|
223
|
+
rubygems_version: 3.5.22
|
|
224
|
+
signing_key:
|
|
225
|
+
specification_version: 4
|
|
226
|
+
summary: Policy-based archive storage and zero-downtime file migration.
|
|
227
|
+
test_files: []
|