longleaf 0.2.0.pre.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +84 -0
- data/.gitignore +4 -2
- data/.rubocop.yml +42 -2
- data/.rubocop_todo.yml +390 -311
- data/.yardopts +1 -0
- data/Gemfile +16 -1
- data/README.md +67 -13
- data/Rakefile +6 -0
- data/bin/setup +16 -1
- data/docs/aboutlongleaf.md +28 -0
- data/docs/extra.css +32 -0
- data/docs/img/change-file.png +0 -0
- data/docs/img/ll-example-preserved.png +0 -0
- data/docs/index.md +19 -0
- data/docs/install.md +66 -0
- data/docs/ll-example/config-example-relative.yml +33 -0
- data/docs/ll-example/files-dir/LLexample-PDF.pdf +0 -0
- data/docs/ll-example/files-dir/LLexample-TOCHANGE.txt +15 -0
- data/docs/ll-example/files-dir/LLexample-tokeep.txt +10 -0
- data/docs/ll-example/metadata-dir/.gitkeep +0 -0
- data/docs/ll-example/replica-files/.gitkeep +0 -0
- data/docs/ll-example/replica-metadata/.gitkeep +0 -0
- data/docs/quickstart.md +270 -0
- data/docs/rdocs/Longleaf.html +135 -0
- data/docs/rdocs/Longleaf/AppFields.html +178 -0
- data/docs/rdocs/Longleaf/ApplicationConfigDeserializer.html +631 -0
- data/docs/rdocs/Longleaf/ApplicationConfigManager.html +610 -0
- data/docs/rdocs/Longleaf/ApplicationConfigValidator.html +238 -0
- data/docs/rdocs/Longleaf/CLI.html +909 -0
- data/docs/rdocs/Longleaf/ChecksumMismatchError.html +151 -0
- data/docs/rdocs/Longleaf/ConfigBuilder.html +1339 -0
- data/docs/rdocs/Longleaf/ConfigurationError.html +143 -0
- data/docs/rdocs/Longleaf/ConfigurationValidator.html +227 -0
- data/docs/rdocs/Longleaf/DeregisterCommand.html +420 -0
- data/docs/rdocs/Longleaf/DeregisterEvent.html +453 -0
- data/docs/rdocs/Longleaf/DeregistrationError.html +151 -0
- data/docs/rdocs/Longleaf/DigestHelper.html +419 -0
- data/docs/rdocs/Longleaf/EventError.html +147 -0
- data/docs/rdocs/Longleaf/EventNames.html +163 -0
- data/docs/rdocs/Longleaf/EventStatusTracking.html +656 -0
- data/docs/rdocs/Longleaf/FileCheckService.html +540 -0
- data/docs/rdocs/Longleaf/FileHelpers.html +520 -0
- data/docs/rdocs/Longleaf/FileRecord.html +716 -0
- data/docs/rdocs/Longleaf/FileSelector.html +901 -0
- data/docs/rdocs/Longleaf/FixityCheckService.html +691 -0
- data/docs/rdocs/Longleaf/IndexManager.html +1155 -0
- data/docs/rdocs/Longleaf/InvalidDigestAlgorithmError.html +143 -0
- data/docs/rdocs/Longleaf/InvalidStoragePathError.html +143 -0
- data/docs/rdocs/Longleaf/Logging.html +405 -0
- data/docs/rdocs/Longleaf/Logging/RedirectingLogger.html +1213 -0
- data/docs/rdocs/Longleaf/LongleafError.html +139 -0
- data/docs/rdocs/Longleaf/MDFields.html +193 -0
- data/docs/rdocs/Longleaf/MetadataBuilder.html +787 -0
- data/docs/rdocs/Longleaf/MetadataDeserializer.html +537 -0
- data/docs/rdocs/Longleaf/MetadataError.html +143 -0
- data/docs/rdocs/Longleaf/MetadataPersistenceManager.html +539 -0
- data/docs/rdocs/Longleaf/MetadataRecord.html +1411 -0
- data/docs/rdocs/Longleaf/MetadataSerializer.html +786 -0
- data/docs/rdocs/Longleaf/PreservationServiceError.html +147 -0
- data/docs/rdocs/Longleaf/PreserveCommand.html +410 -0
- data/docs/rdocs/Longleaf/PreserveEvent.html +491 -0
- data/docs/rdocs/Longleaf/RegisterCommand.html +428 -0
- data/docs/rdocs/Longleaf/RegisterEvent.html +628 -0
- data/docs/rdocs/Longleaf/RegisteredFileSelector.html +446 -0
- data/docs/rdocs/Longleaf/RegistrationError.html +151 -0
- data/docs/rdocs/Longleaf/ReindexCommand.html +576 -0
- data/docs/rdocs/Longleaf/RsyncReplicationService.html +1180 -0
- data/docs/rdocs/Longleaf/SequelIndexDriver.html +1978 -0
- data/docs/rdocs/Longleaf/ServiceCandidateFilesystemIterator.html +572 -0
- data/docs/rdocs/Longleaf/ServiceCandidateIndexIterator.html +532 -0
- data/docs/rdocs/Longleaf/ServiceCandidateLocator.html +333 -0
- data/docs/rdocs/Longleaf/ServiceClassCache.html +725 -0
- data/docs/rdocs/Longleaf/ServiceDateHelper.html +425 -0
- data/docs/rdocs/Longleaf/ServiceDefinition.html +683 -0
- data/docs/rdocs/Longleaf/ServiceDefinitionManager.html +371 -0
- data/docs/rdocs/Longleaf/ServiceDefinitionValidator.html +269 -0
- data/docs/rdocs/Longleaf/ServiceFields.html +173 -0
- data/docs/rdocs/Longleaf/ServiceManager.html +1229 -0
- data/docs/rdocs/Longleaf/ServiceMappingManager.html +410 -0
- data/docs/rdocs/Longleaf/ServiceMappingValidator.html +347 -0
- data/docs/rdocs/Longleaf/ServiceRecord.html +821 -0
- data/docs/rdocs/Longleaf/StorageLocation.html +985 -0
- data/docs/rdocs/Longleaf/StorageLocationManager.html +729 -0
- data/docs/rdocs/Longleaf/StorageLocationUnavailableError.html +143 -0
- data/docs/rdocs/Longleaf/StorageLocationValidator.html +373 -0
- data/docs/rdocs/Longleaf/StoragePathValidator.html +253 -0
- data/docs/rdocs/Longleaf/SystemConfigBuilder.html +441 -0
- data/docs/rdocs/Longleaf/SystemConfigFields.html +163 -0
- data/docs/rdocs/Longleaf/ValidateConfigCommand.html +451 -0
- data/docs/rdocs/Longleaf/ValidateMetadataCommand.html +408 -0
- data/docs/rdocs/_index.html +660 -0
- data/docs/rdocs/class_list.html +51 -0
- data/docs/rdocs/css/common.css +1 -0
- data/docs/rdocs/css/full_list.css +58 -0
- data/docs/rdocs/css/style.css +496 -0
- data/docs/rdocs/file.README.html +165 -0
- data/docs/rdocs/file_list.html +56 -0
- data/docs/rdocs/frames.html +17 -0
- data/docs/rdocs/index.html +165 -0
- data/docs/rdocs/js/app.js +303 -0
- data/docs/rdocs/js/full_list.js +216 -0
- data/docs/rdocs/js/jquery.js +4 -0
- data/docs/rdocs/method_list.html +2051 -0
- data/docs/rdocs/top-level-namespace.html +110 -0
- data/lib/longleaf/candidates/file_selector.rb +47 -15
- data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
- data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +29 -35
- data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
- data/lib/longleaf/candidates/service_candidate_locator.rb +9 -4
- data/lib/longleaf/cli.rb +162 -80
- data/lib/longleaf/commands/deregister_command.rb +12 -11
- data/lib/longleaf/commands/preserve_command.rb +13 -8
- data/lib/longleaf/commands/register_command.rb +9 -6
- data/lib/longleaf/commands/reindex_command.rb +92 -0
- data/lib/longleaf/commands/validate_config_command.rb +27 -6
- data/lib/longleaf/commands/validate_metadata_command.rb +11 -9
- data/lib/longleaf/errors.rb +12 -12
- data/lib/longleaf/events/deregister_event.rb +13 -15
- data/lib/longleaf/events/event_status_tracking.rb +7 -7
- data/lib/longleaf/events/preserve_event.rb +24 -14
- data/lib/longleaf/events/register_event.rb +21 -35
- data/lib/longleaf/helpers/digest_helper.rb +4 -4
- data/lib/longleaf/helpers/service_date_helper.rb +5 -6
- data/lib/longleaf/indexing/index_manager.rb +101 -0
- data/lib/longleaf/indexing/sequel_index_driver.rb +324 -0
- data/lib/longleaf/logging.rb +4 -4
- data/lib/longleaf/logging/redirecting_logger.rb +20 -20
- data/lib/longleaf/models/app_fields.rb +2 -1
- data/lib/longleaf/models/file_record.rb +10 -6
- data/lib/longleaf/models/md_fields.rb +1 -1
- data/lib/longleaf/models/metadata_record.rb +22 -12
- data/lib/longleaf/models/service_definition.rb +3 -3
- data/lib/longleaf/models/service_fields.rb +1 -1
- data/lib/longleaf/models/service_record.rb +6 -5
- data/lib/longleaf/models/storage_location.rb +26 -7
- data/lib/longleaf/models/system_config_fields.rb +9 -0
- data/lib/longleaf/preservation_services/file_check_service.rb +58 -0
- data/lib/longleaf/preservation_services/fixity_check_service.rb +16 -14
- data/lib/longleaf/preservation_services/rsync_replication_service.rb +32 -31
- data/lib/longleaf/services/application_config_deserializer.rb +55 -18
- data/lib/longleaf/services/application_config_manager.rb +16 -4
- data/lib/longleaf/services/application_config_validator.rb +1 -2
- data/lib/longleaf/services/configuration_validator.rb +6 -4
- data/lib/longleaf/services/metadata_deserializer.rb +40 -38
- data/lib/longleaf/services/metadata_persistence_manager.rb +46 -0
- data/lib/longleaf/services/metadata_serializer.rb +23 -22
- data/lib/longleaf/services/service_class_cache.rb +15 -15
- data/lib/longleaf/services/service_definition_manager.rb +5 -6
- data/lib/longleaf/services/service_definition_validator.rb +5 -6
- data/lib/longleaf/services/service_manager.rb +37 -17
- data/lib/longleaf/services/service_mapping_manager.rb +9 -9
- data/lib/longleaf/services/service_mapping_validator.rb +9 -10
- data/lib/longleaf/services/storage_location_manager.rb +22 -8
- data/lib/longleaf/services/storage_location_validator.rb +11 -8
- data/lib/longleaf/services/storage_path_validator.rb +1 -1
- data/lib/longleaf/specs/config_builder.rb +30 -17
- data/lib/longleaf/specs/custom_matchers.rb +1 -1
- data/lib/longleaf/specs/file_helpers.rb +15 -14
- data/lib/longleaf/specs/metadata_builder.rb +91 -0
- data/lib/longleaf/specs/system_config_builder.rb +27 -0
- data/lib/longleaf/version.rb +1 -1
- data/longleaf.gemspec +17 -7
- data/mkdocs.yml +20 -0
- metadata +233 -22
data/lib/longleaf/logging.rb
CHANGED
@@ -7,16 +7,16 @@ module Longleaf
|
|
7
7
|
def logger
|
8
8
|
Logging.logger
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
# Get the main logger for longleaf
|
12
12
|
def self.logger
|
13
13
|
@logger ||= RedirectingLogger.new
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def initialize_logger(failure_only, log_level, log_format, datetime_format)
|
17
17
|
Logging.initialize_logger(failure_only, log_level, log_format, datetime_format)
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def self.initialize_logger(failure_only, log_level, log_format, datetime_format)
|
21
21
|
@logger = RedirectingLogger.new(failure_only: failure_only,
|
22
22
|
log_level: log_level,
|
@@ -24,4 +24,4 @@ module Longleaf
|
|
24
24
|
datetime_format: datetime_format)
|
25
25
|
end
|
26
26
|
end
|
27
|
-
end
|
27
|
+
end
|
@@ -25,12 +25,12 @@ module Longleaf
|
|
25
25
|
@stderr_log.formatter = proc do |severity, datetime, progname, msg|
|
26
26
|
# Make sure the format ends with a newline
|
27
27
|
@log_format = @log_format + "\n" unless @log_format.end_with?("\n")
|
28
|
-
|
28
|
+
|
29
29
|
formatted_date = @stderr_log.datetime_format.nil? ? datetime : datetime.strftime(datetime_format)
|
30
30
|
@log_format % { :severity => severity, :datetime => formatted_date, :progname => progname, :msg => msg }
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
@stdout_log = Logger.new($stdout)
|
35
35
|
@stdout_log.formatter = proc do |severity, datetime, progname, msg|
|
36
36
|
"#{msg}\n"
|
@@ -41,34 +41,34 @@ module Longleaf
|
|
41
41
|
@stdout_log.level = 'info'
|
42
42
|
end
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def debug(progname = nil, &block)
|
46
46
|
@stderr_log.debug(progname, &block)
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
def info(progname = nil, &block)
|
50
50
|
@stderr_log.info(progname, &block)
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
def warn(progname = nil, &block)
|
54
54
|
@stderr_log.warn(progname, &block)
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
def error(progname = nil, &block)
|
58
58
|
@stderr_log.error(progname, &block)
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
def fatal(progname = nil, &block)
|
62
62
|
@stderr_log.fatal(progname, &block)
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
def unknown(progname = nil, &block)
|
66
66
|
@stderr_log.unknown(progname, &block)
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
# Logs a success message to STDOUT, as well as STDERR at info level.
|
70
|
-
#
|
71
|
-
# @param [String] eventOrMessage name of the preservation event which succeeded,
|
70
|
+
#
|
71
|
+
# @param [String] eventOrMessage name of the preservation event which succeeded,
|
72
72
|
# or the message to output if it is the only parameter. Required.
|
73
73
|
# @param file_name [String] file name which is the subject of this message.
|
74
74
|
# @param message [String] descriptive message to accompany this output
|
@@ -76,10 +76,10 @@ module Longleaf
|
|
76
76
|
def success(eventOrMessage, file_name = nil, message = nil, service = nil)
|
77
77
|
outcome('SUCCESS', eventOrMessage, file_name, message, service)
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
# Logs a failure message to STDOUT, as well as STDERR at info level.
|
81
81
|
# If an error was provided, it is logged to STDERR at error level.
|
82
|
-
# @param eventOrMessage [String] name of the preservation event which failed,
|
82
|
+
# @param eventOrMessage [String] name of the preservation event which failed,
|
83
83
|
# or the message to output if it is the only parameter.
|
84
84
|
# @param file_name [String] file name which is the subject of this message.
|
85
85
|
# @param message [String] descriptive message to accompany this output
|
@@ -88,17 +88,17 @@ module Longleaf
|
|
88
88
|
def failure(eventOrMessage, file_name = nil, message = nil, service = nil, error: nil)
|
89
89
|
text = outcome_text('FAILURE', eventOrMessage, file_name, message, service, error)
|
90
90
|
@stdout_log.warn(text)
|
91
|
-
|
91
|
+
|
92
92
|
@stderr_log.info(text)
|
93
93
|
@stderr_log.error("#{error.message}") unless error.nil?
|
94
|
-
@stderr_log.error("#{error.backtrace
|
94
|
+
@stderr_log.error("#{error.backtrace}") unless error.nil? || error.backtrace.nil?
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
# Logs an outcome message to STDOUT, as well as STDERR at info level.
|
98
98
|
# If file_name and message are nil, eventOrMessage will be used as the message.
|
99
99
|
#
|
100
100
|
# @param outcome [String] The status of the outcome. Required.
|
101
|
-
# @param eventOrMessage [String] name of the preservation event which was successful,
|
101
|
+
# @param eventOrMessage [String] name of the preservation event which was successful,
|
102
102
|
# or the message to output if it is the only parameter. Required.
|
103
103
|
# @param file_name [String] file name which is the subject of this message.
|
104
104
|
# @param message [String] descriptive message to accompany this output
|
@@ -109,13 +109,13 @@ module Longleaf
|
|
109
109
|
@stdout_log.info(text)
|
110
110
|
@stderr_log.info(text)
|
111
111
|
end
|
112
|
-
|
112
|
+
|
113
113
|
private
|
114
114
|
def outcome_text(outcome, eventOrMessage, file_name = nil, message = nil, service = nil, error = nil)
|
115
115
|
message_only = file_name.nil? && message.nil? && error.nil?
|
116
|
-
|
116
|
+
|
117
117
|
text = "#{outcome}"
|
118
|
-
|
118
|
+
|
119
119
|
if message_only
|
120
120
|
text << ": #{eventOrMessage}"
|
121
121
|
else
|
@@ -1,30 +1,34 @@
|
|
1
1
|
module Longleaf
|
2
2
|
# Record for an individual file and its associated information
|
3
3
|
class FileRecord
|
4
|
-
|
5
4
|
attr_accessor :metadata_record
|
6
5
|
attr_reader :storage_location
|
7
6
|
attr_reader :path
|
8
|
-
|
7
|
+
|
9
8
|
# @param file_path [String] path to the file
|
10
9
|
# @param storage_location [StorageLocation] storage location containing the file
|
11
10
|
def initialize(file_path, storage_location, metadata_record = nil)
|
12
11
|
raise ArgumentError.new("FileRecord requires a path") if file_path.nil?
|
13
12
|
raise ArgumentError.new("FileRecord requires a storage_location") if storage_location.nil?
|
14
|
-
|
13
|
+
|
15
14
|
@path = file_path
|
16
15
|
@storage_location = storage_location
|
17
16
|
@metadata_record = metadata_record
|
18
17
|
end
|
19
|
-
|
18
|
+
|
20
19
|
# @return [String] path for the metadata file for this file
|
21
20
|
def metadata_path
|
22
21
|
@metadata_path = @storage_location.get_metadata_path_for(path) if @metadata_path.nil?
|
23
22
|
@metadata_path
|
24
23
|
end
|
25
|
-
|
24
|
+
|
26
25
|
def metadata_present?
|
27
26
|
File.exist?(metadata_path)
|
28
27
|
end
|
28
|
+
|
29
|
+
def ==(other_obj)
|
30
|
+
return false unless other_obj.is_a?(FileRecord)
|
31
|
+
path == other_obj.path
|
32
|
+
end
|
29
33
|
end
|
30
|
-
end
|
34
|
+
end
|
@@ -9,7 +9,7 @@ module Longleaf
|
|
9
9
|
attr_reader :checksums
|
10
10
|
attr_reader :properties
|
11
11
|
attr_accessor :file_size, :last_modified
|
12
|
-
|
12
|
+
|
13
13
|
# @param properties [Hash] initial data properties for this record
|
14
14
|
# @param services [Hash] initial service property tree
|
15
15
|
# @param deregistered [String] deregistered timestamp
|
@@ -17,22 +17,22 @@ module Longleaf
|
|
17
17
|
# @param checksums [Hash] hash of checksum values
|
18
18
|
# @param file_size [Integer] size of file in bytes
|
19
19
|
# @param last_modified [String] iso8601 representation of the last modified date of file
|
20
|
-
def initialize(properties:
|
20
|
+
def initialize(properties: nil, services: nil, deregistered: nil, registered: nil, checksums: nil,
|
21
21
|
file_size: nil, last_modified: nil)
|
22
|
-
@properties = properties
|
22
|
+
@properties = properties || Hash.new
|
23
23
|
@registered = registered
|
24
24
|
@deregistered = deregistered
|
25
|
-
@checksums = checksums
|
26
|
-
@services = services
|
25
|
+
@checksums = checksums || Hash.new
|
26
|
+
@services = services || Hash.new
|
27
27
|
@file_size = file_size
|
28
28
|
@last_modified = last_modified
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# @return [Boolean] true if the record is deregistered
|
32
32
|
def deregistered?
|
33
33
|
!@deregistered.nil?
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
# Adds a service to this record
|
37
37
|
#
|
38
38
|
# @param name [String] identifier for the service being added
|
@@ -41,10 +41,10 @@ module Longleaf
|
|
41
41
|
def add_service(name, service = ServiceRecord.new)
|
42
42
|
raise ArgumentError.new("Value must be a ServiceRecord object when adding a service") unless service.class == Longleaf::ServiceRecord
|
43
43
|
raise IndexError.new("Service with name '#{name}' already exists") if @services.key?(name)
|
44
|
-
|
44
|
+
|
45
45
|
@services[name] = service
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
# Updates details of service record as if the service had been executed.
|
49
49
|
# @param service_name [String] name of the service run
|
50
50
|
# @return [ServiceRecord] the service record updated
|
@@ -54,16 +54,26 @@ module Longleaf
|
|
54
54
|
service_rec.timestamp = ServiceDateHelper.formatted_timestamp
|
55
55
|
service_rec
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
|
+
# Updates details of service record as if the service had encountered a
|
59
|
+
# failure during execution.
|
60
|
+
# @param service_name [String] name of the service run
|
61
|
+
# @return [ServiceRecord] the service record updated
|
62
|
+
def update_service_as_failed(service_name)
|
63
|
+
service_rec = service(service_name) || add_service(service_name)
|
64
|
+
service_rec.failure_timestamp = ServiceDateHelper.formatted_timestamp
|
65
|
+
service_rec
|
66
|
+
end
|
67
|
+
|
58
68
|
# @param name [String] name identifier of the service to retrieve
|
59
69
|
# @return [ServiceRecord] the ServiceRecord for the service identified by name, or nil
|
60
70
|
def service(name)
|
61
71
|
@services[name]
|
62
72
|
end
|
63
|
-
|
73
|
+
|
64
74
|
# @return [Array<String>] a list of name identifiers for services registered to this record
|
65
75
|
def list_services
|
66
76
|
@services.keys
|
67
77
|
end
|
68
78
|
end
|
69
|
-
end
|
79
|
+
end
|
@@ -7,10 +7,10 @@ module Longleaf
|
|
7
7
|
attr_reader :work_script, :work_class
|
8
8
|
attr_reader :frequency, :delay
|
9
9
|
attr_reader :properties
|
10
|
-
|
10
|
+
|
11
11
|
def initialize(name:, work_script:, work_class: nil, frequency: nil, delay: nil, properties: Hash.new)
|
12
12
|
raise ArgumentError.new("Parameters name and work_script are required") unless name && work_script
|
13
|
-
|
13
|
+
|
14
14
|
@properties = properties
|
15
15
|
@name = name
|
16
16
|
@work_script = work_script
|
@@ -19,4 +19,4 @@ module Longleaf
|
|
19
19
|
@delay = delay
|
20
20
|
end
|
21
21
|
end
|
22
|
-
end
|
22
|
+
end
|
@@ -3,28 +3,29 @@ module Longleaf
|
|
3
3
|
class ServiceRecord
|
4
4
|
attr_reader :properties
|
5
5
|
attr_accessor :stale_replicas, :timestamp, :run_needed
|
6
|
-
|
6
|
+
attr_accessor :failure_timestamp
|
7
|
+
|
7
8
|
# @param properties [Hash] initial properties for this service record
|
8
9
|
# @param stale_replicas [Boolean] whether there are any stale replicas from this service
|
9
10
|
# @param timestamp [String] timestamp when this service last ran or was initialized
|
10
11
|
# @param run_needed [Boolean] flag indicating that this service should be run at the next available opportunity
|
11
12
|
def initialize(properties: Hash.new, stale_replicas: false, timestamp: nil, run_needed: false)
|
12
13
|
raise ArgumentError.new("Service properties must be a hash") if properties.class != Hash
|
13
|
-
|
14
|
+
|
14
15
|
@properties = properties
|
15
16
|
@timestamp = timestamp
|
16
17
|
@stale_replicas = stale_replicas
|
17
18
|
@run_needed = run_needed
|
18
19
|
end
|
19
|
-
|
20
|
+
|
20
21
|
# @return the value of a service property identified by key
|
21
22
|
def [](key)
|
22
23
|
@properties[key]
|
23
24
|
end
|
24
|
-
|
25
|
+
|
25
26
|
# set the value of a service property identified by key
|
26
27
|
def []=(key, value)
|
27
28
|
@properties[key] = value
|
28
29
|
end
|
29
30
|
end
|
30
|
-
end
|
31
|
+
end
|
@@ -7,20 +7,20 @@ module Longleaf
|
|
7
7
|
attr_reader :path
|
8
8
|
attr_reader :metadata_path
|
9
9
|
attr_reader :metadata_digests
|
10
|
-
|
10
|
+
|
11
11
|
# @param name [String] the name of this storage location
|
12
12
|
# @param path [String] absolute path where the storage location is located
|
13
13
|
# @param metadata_path [String] absolute path where the metadata for files in this location will be stored.
|
14
14
|
# @param metadata_digests list of digest algorithms to use for metadata file digests in this location.
|
15
15
|
def initialize(name:, path:, metadata_path:, metadata_digests: [])
|
16
16
|
raise ArgumentError.new("Parameters name, path and metadata_path are required") unless name && path && metadata_path
|
17
|
-
|
17
|
+
|
18
18
|
@path = path
|
19
19
|
@path += '/' unless @path.end_with?('/')
|
20
20
|
@name = name
|
21
21
|
@metadata_path = metadata_path
|
22
22
|
@metadata_path += '/' unless @metadata_path.end_with?('/')
|
23
|
-
|
23
|
+
|
24
24
|
if metadata_digests.nil?
|
25
25
|
@metadata_digests = []
|
26
26
|
elsif metadata_digests.is_a?(String)
|
@@ -30,7 +30,7 @@ module Longleaf
|
|
30
30
|
end
|
31
31
|
DigestHelper::validate_algorithms(@metadata_digests)
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
# Get the path for the metadata file for the given file path located in this storage location.
|
35
35
|
# @param file_path [String] path of the file
|
36
36
|
# @raise [ArgumentError] if the file_path is not provided or is not in this storage location.
|
@@ -39,9 +39,28 @@ module Longleaf
|
|
39
39
|
raise ArgumentError.new("Provided file path is not contained by storage location #{@name}: #{file_path}") \
|
40
40
|
unless file_path.start_with?(@path)
|
41
41
|
|
42
|
-
file_path.sub(/^#{@path}/, metadata_path)
|
42
|
+
md_path = file_path.sub(/^#{@path}/, @metadata_path)
|
43
|
+
# If the file_path is to a file, then add metadata suffix.
|
44
|
+
if md_path.end_with?('/')
|
45
|
+
md_path
|
46
|
+
else
|
47
|
+
md_path + MetadataSerializer::metadata_suffix
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get the metadata path for the provided file path located in this storage location.
|
52
|
+
# @param md_path [String] metadata file path
|
53
|
+
# @raise [ArgumentError] if the md_path is not in this storage location
|
54
|
+
# @return [String] the path for the file associated with this metadata
|
55
|
+
def get_path_from_metadata_path(md_path)
|
56
|
+
raise ArgumentError.new("A file_path parameter is required") if md_path.nil? || md_path.empty?
|
57
|
+
raise ArgumentError.new("Provided metadata path is not contained by storage location #{@name}: #{md_path}") \
|
58
|
+
unless md_path&.start_with?(@metadata_path)
|
59
|
+
|
60
|
+
file_path = md_path.sub(/^#{@metadata_path}/, @path)
|
61
|
+
file_path.sub(/#{MetadataSerializer::metadata_suffix}$/, '')
|
43
62
|
end
|
44
|
-
|
63
|
+
|
45
64
|
# Checks that the path and metadata path defined in this location are available
|
46
65
|
# @raise [StorageLocationUnavailableError] if the storage location is not available
|
47
66
|
def available?
|
@@ -51,4 +70,4 @@ module Longleaf
|
|
51
70
|
unless Dir.exist?(@metadata_path)
|
52
71
|
end
|
53
72
|
end
|
54
|
-
end
|
73
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'longleaf/events/event_names'
|
2
|
+
require 'longleaf/logging'
|
3
|
+
|
4
|
+
module Longleaf
|
5
|
+
# Preservation service which validates a file using current filesystem information compared against the
|
6
|
+
# last registered details for that file. Checks using file name, size and last modified timestamp.
|
7
|
+
class FileCheckService
|
8
|
+
include Longleaf::Logging
|
9
|
+
|
10
|
+
# Initialize a FileCheckService from the given service definition
|
11
|
+
#
|
12
|
+
# @param service_def [ServiceDefinition] the configuration for this service
|
13
|
+
# @param app_manager [ApplicationConfigManager] manager for configured storage locations
|
14
|
+
def initialize(service_def, app_manager)
|
15
|
+
@service_def = service_def
|
16
|
+
@app_manager = app_manager
|
17
|
+
end
|
18
|
+
|
19
|
+
# Perform file information check.
|
20
|
+
#
|
21
|
+
# @param file_rec [FileRecord] record representing the file to perform the service on.
|
22
|
+
# @param event [String] name of the event this service is being invoked by.
|
23
|
+
# @raise [PreservationServiceError] if the file system information does not match the stored details
|
24
|
+
def perform(file_rec, event)
|
25
|
+
file_path = file_rec.path
|
26
|
+
md_rec = file_rec.metadata_record
|
27
|
+
|
28
|
+
logger.debug("Performing file information check of #{file_path}")
|
29
|
+
|
30
|
+
if !File.exist?(file_path)
|
31
|
+
raise PreservationServiceError.new("File does not exist: #{file_path}")
|
32
|
+
end
|
33
|
+
|
34
|
+
file_size = File.size(file_rec.path)
|
35
|
+
if file_size != md_rec.file_size
|
36
|
+
raise PreservationServiceError.new("File size for #{file_path} does not match the expected value: registered = #{md_rec.file_size} bytes, actual = #{file_size} bytes")
|
37
|
+
end
|
38
|
+
|
39
|
+
last_modified = File.mtime(file_rec.path).utc.iso8601(3)
|
40
|
+
if last_modified != md_rec.last_modified
|
41
|
+
raise PreservationServiceError.new("Last modified timestamp for #{file_path} does not match the expected value: registered = #{md_rec.last_modified}, actual = #{last_modified}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Determine if this service is applicable for the provided event, given the configured service definition
|
46
|
+
#
|
47
|
+
# @param event [String] name of the event
|
48
|
+
# @return [Boolean] returns true if this service is applicable for the provided event
|
49
|
+
def is_applicable?(event)
|
50
|
+
case event
|
51
|
+
when EventNames::PRESERVE
|
52
|
+
true
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|